From 064b0a6292f84a7518c10d47b95b7f0554402502 Mon Sep 17 00:00:00 2001 From: LuisLainez <71238487+LuisLainez@users.noreply.github.com> Date: Wed, 1 Nov 2023 03:08:06 +1100 Subject: [PATCH 001/202] Issue/upgrade to spring (#3828) SB3 upgrades --- annotations-processor/build.gradle | 4 +- annotations-processor/dependencies.lock | 88 +++--- .../protogen/ProtoGen.java | 2 +- annotations/dependencies.lock | 50 ++-- awss3-storage/dependencies.lock | 96 +++---- awssqs-event-queue/dependencies.lock | 96 +++---- build.gradle | 6 +- cassandra-persistence/build.gradle | 2 +- cassandra-persistence/dependencies.lock | 90 +++---- .../cache/CacheableEventHandlerDAO.java | 4 +- .../config/cache/CacheableMetadataDAO.java | 4 +- client-spring/dependencies.lock | 94 +++---- client/build.gradle | 2 +- client/dependencies.lock | 98 +++---- common/dependencies.lock | 110 ++++---- .../config/ObjectMapperConfiguration.java | 3 +- .../constraints/NoSemiColonConstraint.java | 10 +- .../OwnerEmailMandatoryConstraint.java | 10 +- .../TaskReferenceNameUniqueConstraint.java | 10 +- .../constraints/TaskTimeoutConstraint.java | 10 +- .../common/metadata/events/EventHandler.java | 7 +- .../common/metadata/tasks/TaskDef.java | 12 +- .../common/metadata/tasks/TaskResult.java | 3 +- .../workflow/StartWorkflowRequest.java | 10 +- .../metadata/workflow/SubWorkflowParams.java | 5 +- .../common/metadata/workflow/WorkflowDef.java | 14 +- .../metadata/workflow/WorkflowDefSummary.java | 4 +- .../metadata/workflow/WorkflowTask.java | 7 +- .../conductor/common/run/Workflow.java | 6 +- .../conductor/common/utils/SummaryUtil.java | 3 +- .../common/events/EventHandlerTest.java | 10 +- .../conductor/common/tasks/TaskDefTest.java | 10 +- .../workflow/SubWorkflowParamsTest.java | 9 +- .../workflow/WorkflowDefValidatorTest.java | 10 +- .../common/workflow/WorkflowTaskTest.java | 10 +- core/build.gradle | 2 +- core/dependencies.lock | 135 ++++------ .../core/dal/ExecutionDAOFacade.java | 3 +- .../core/execution/tasks/StartWorkflow.java | 3 +- .../conductor/service/AdminService.java | 4 +- .../conductor/service/EventService.java | 8 +- .../conductor/service/MetadataService.java | 10 +- .../conductor/service/TaskService.java | 8 +- .../service/WorkflowBulkService.java | 6 +- .../conductor/service/WorkflowService.java | 12 +- .../WorkflowTaskTypeConstraint.java | 10 +- .../IsolatedTaskQueueProducerSpec.groovy | 3 - .../execution/tasks/StartWorkflowSpec.groovy | 5 +- .../java/com/netflix/conductor/TestUtils.java | 2 +- .../metadata/MetadataMapperServiceTest.java | 4 +- .../conductor/service/EventServiceTest.java | 4 +- .../service/MetadataServiceTest.java | 4 +- .../conductor/service/TaskServiceTest.java | 4 +- .../service/WorkflowBulkServiceTest.java | 4 +- .../service/WorkflowServiceTest.java | 4 +- .../WorkflowDefConstraintTest.java | 16 +- .../WorkflowTaskTypeConstraintTest.java | 18 +- dependencies.gradle | 15 +- dependencies.lock | 50 ++-- es6-persistence/build.gradle | 6 +- es6-persistence/dependencies.lock | 72 ++--- .../es6/dao/index/ElasticSearchDAOV6.java | 5 +- .../es6/dao/index/ElasticSearchRestDAOV6.java | 5 +- grpc-client/build.gradle | 1 + grpc-client/dependencies.lock | 120 ++++----- .../conductor/client/grpc/ClientBase.java | 3 +- .../conductor/client/grpc/MetadataClient.java | 3 +- .../conductor/client/grpc/TaskClient.java | 3 +- .../conductor/client/grpc/WorkflowClient.java | 3 +- grpc-server/dependencies.lock | 132 ++++----- .../conductor/grpc/server/GRPCServer.java | 5 +- .../grpc/server/service/GRPCHelper.java | 3 +- grpc/build.gradle | 8 +- grpc/dependencies.lock | 160 ++++++----- .../conductor/grpc/AbstractProtoMapper.java | 2 +- .../conductor/grpc/TestProtoMapper.java | 2 +- http-task/build.gradle | 1 + http-task/dependencies.lock | 114 ++++---- .../conductor/tasks/http/HttpTask.java | 15 +- .../DefaultRestTemplateProvider.java | 21 +- .../conductor/tasks/http/HttpTaskTest.java | 2 +- .../DefaultRestTemplateProviderTest.java | 2 + java-sdk/build.gradle | 2 +- java-sdk/dependencies.lock | 100 +++---- json-jq-task/dependencies.lock | 100 +++---- redis-concurrency-limit/build.gradle | 2 +- redis-concurrency-limit/dependencies.lock | 102 ++++--- redis-lock/dependencies.lock | 96 +++---- redis-persistence/dependencies.lock | 96 +++---- .../dynoqueue/ConfigurationHostSupplier.java | 3 + rest/dependencies.lock | 102 +++---- .../ApplicationExceptionMapper.java | 3 +- .../ValidationExceptionMapper.java | 10 +- server/build.gradle | 1 + server/dependencies.lock | 250 +++++++++--------- springboot-bom-overrides.gradle | 2 +- test-harness/build.gradle | 2 +- test-harness/dependencies.lock | 102 +++---- .../test/util/WorkflowTestUtil.groovy | 3 +- .../http/AbstractHttpEndToEndTest.java | 2 +- 100 files changed, 1448 insertions(+), 1461 deletions(-) diff --git a/annotations-processor/build.gradle b/annotations-processor/build.gradle index 2bb9a52aa..5e0c28d3b 100644 --- a/annotations-processor/build.gradle +++ b/annotations-processor/build.gradle @@ -8,8 +8,8 @@ dependencies { api 'com.google.guava:guava:31.1-jre' api 'com.squareup:javapoet:1.13.+' api 'com.github.jknack:handlebars:4.3.+' - api 'com.google.protobuf:protobuf-java:3.21.7' - api 'javax.annotation:javax.annotation-api:1.3.2' + api 'com.google.protobuf:protobuf-java:3.21.12' + api 'jakarta.annotation:jakarta.annotation-api:2.1.1' api gradleApi() exampleImplementation sourceSets.main.output diff --git a/annotations-processor/dependencies.lock b/annotations-processor/dependencies.lock index 481b791d0..f0c8b22ee 100644 --- a/annotations-processor/dependencies.lock +++ b/annotations-processor/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -12,7 +12,7 @@ "locked": "31.1-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -20,23 +20,23 @@ "com.squareup:javapoet": { "locked": "1.13.0" }, - "javax.annotation:javax.annotation-api": { - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" } }, "exampleCompileClasspath": { @@ -52,31 +52,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "runtimeClasspath": { @@ -87,7 +87,7 @@ "locked": "31.1-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -95,38 +95,38 @@ "com.squareup:javapoet": { "locked": "1.13.0" }, - "javax.annotation:javax.annotation-api": { - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "testCompileClasspath": { @@ -137,7 +137,7 @@ "locked": "31.1-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -145,35 +145,35 @@ "com.squareup:javapoet": { "locked": "1.13.0" }, - "javax.annotation:javax.annotation-api": { - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "junit:junit": { "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -184,7 +184,7 @@ "locked": "31.1-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -192,8 +192,8 @@ "com.squareup:javapoet": { "locked": "1.13.0" }, - "javax.annotation:javax.annotation-api": { - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "junit:junit": { "locked": "4.13.2" @@ -202,40 +202,40 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java index a2550d369..e694918c3 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java @@ -20,7 +20,6 @@ import java.net.URLClassLoader; import java.util.*; -import javax.annotation.Generated; import javax.lang.model.element.Modifier; import com.netflix.conductor.annotations.protogen.ProtoMessage; @@ -35,6 +34,7 @@ import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; +import jakarta.annotation.Generated; public class ProtoGen { private static final String GENERATOR_NAME = diff --git a/annotations/dependencies.lock b/annotations/dependencies.lock index 7b2c9a2ac..58dd8ff8a 100644 --- a/annotations/dependencies.lock +++ b/annotations/dependencies.lock @@ -1,41 +1,41 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "testCompileClasspath": { @@ -43,28 +43,28 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -72,28 +72,28 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/awss3-storage/dependencies.lock b/awss3-storage/dependencies.lock index b3c1cb307..c8239eb17 100644 --- a/awss3-storage/dependencies.lock +++ b/awss3-storage/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -18,22 +18,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -44,46 +44,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -122,19 +122,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +188,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -214,28 +214,28 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -246,46 +246,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -324,19 +324,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -361,7 +361,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -369,7 +369,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -377,7 +377,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -385,7 +385,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -393,10 +393,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -405,10 +405,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/awssqs-event-queue/dependencies.lock b/awssqs-event-queue/dependencies.lock index 8e9e5e532..3bb1559cd 100644 --- a/awssqs-event-queue/dependencies.lock +++ b/awssqs-event-queue/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -24,22 +24,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -50,33 +50,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -86,13 +86,13 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -137,13 +137,13 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -165,7 +165,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -173,7 +173,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -181,7 +181,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -189,7 +189,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -197,7 +197,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -229,31 +229,31 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -264,33 +264,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -300,13 +300,13 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -351,13 +351,13 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -382,7 +382,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -390,7 +390,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -398,7 +398,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -406,7 +406,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -414,10 +414,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -426,13 +426,13 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6e6ea6f6e..34041e57e 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:7.0.0' - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.7.3' + classpath 'org.springframework.boot:spring-boot-gradle-plugin:3.1.4' classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.+' } } @@ -90,7 +90,7 @@ allprojects { dependencyManagement { imports { - // dependency versions for the BOM can be found at https://docs.spring.io/spring-boot/docs/2.7.3/reference/htmlsingle/#appendix.dependency-versions + // dependency versions for the BOM can be found at https://docs.spring.io/spring-boot/docs/3.1.4/reference/htmlsingle/#appendix.dependency-versions mavenBom(SpringBootPlugin.BOM_COORDINATES) } } @@ -175,4 +175,4 @@ configure(allprojects - project(':conductor-grpc')) { } } } -} +} \ No newline at end of file diff --git a/cassandra-persistence/build.gradle b/cassandra-persistence/build.gradle index 6fee6f8ff..1d6745c61 100644 --- a/cassandra-persistence/build.gradle +++ b/cassandra-persistence/build.gradle @@ -23,7 +23,7 @@ dependencies { testImplementation project(':conductor-core').sourceSets.test.output testImplementation project(':conductor-common').sourceSets.test.output - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" testImplementation "org.testcontainers:spock:${revTestContainer}" diff --git a/cassandra-persistence/dependencies.lock b/cassandra-persistence/dependencies.lock index 66f9c31d8..f7503712c 100644 --- a/cassandra-persistence/dependencies.lock +++ b/cassandra-persistence/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -18,22 +18,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -44,27 +44,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -77,7 +77,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +188,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -202,7 +202,7 @@ "locked": "3.10.2" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true @@ -217,37 +217,37 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testcontainers:cassandra": { "locked": "1.15.3" @@ -264,27 +264,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -297,7 +297,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -379,7 +379,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -387,7 +387,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -395,7 +395,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -403,7 +403,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -411,10 +411,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" @@ -426,16 +426,16 @@ "locked": "15.4" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testcontainers:cassandra": { "locked": "1.15.3" diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java index 6f4b8bee1..fabfd3c55 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java @@ -20,8 +20,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache; @@ -36,6 +34,8 @@ import com.netflix.conductor.dao.EventHandlerDAO; import com.netflix.conductor.metrics.Monitors; +import jakarta.annotation.PostConstruct; + import static com.netflix.conductor.cassandra.config.cache.CachingConfig.EVENT_HANDLER_CACHE; @Trace diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java index 256512ea4..2facedbd0 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java @@ -20,8 +20,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache; @@ -38,6 +36,8 @@ import com.netflix.conductor.dao.MetadataDAO; import com.netflix.conductor.metrics.Monitors; +import jakarta.annotation.PostConstruct; + import static com.netflix.conductor.cassandra.config.cache.CachingConfig.TASK_DEF_CACHE; @Trace diff --git a/client-spring/dependencies.lock b/client-spring/dependencies.lock index 4e7ca32b3..c9914c60a 100644 --- a/client-spring/dependencies.lock +++ b/client-spring/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -18,22 +18,22 @@ "locked": "1.10.10" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -53,32 +53,32 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -90,7 +90,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -166,7 +166,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -175,7 +175,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -184,7 +184,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -193,7 +193,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -202,14 +202,14 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.35" + "locked": "3.1.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -221,10 +221,10 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.7.36" + "locked": "2.0.9" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testCompileClasspath": { @@ -244,31 +244,31 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -288,32 +288,32 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -325,7 +325,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -404,7 +404,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -413,7 +413,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -422,7 +422,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -431,7 +431,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -440,17 +440,17 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.35" + "locked": "3.1.3" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -462,16 +462,16 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.7.36" + "locked": "2.0.9" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/client/build.gradle b/client/build.gradle index b16dd4e18..2ffd092ed 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -40,7 +40,7 @@ dependencies { testImplementation "org.powermock:powermock-module-junit4:${revPowerMock}" testImplementation "org.powermock:powermock-api-mockito2:${revPowerMock}" - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" } diff --git a/client/dependencies.lock b/client/dependencies.lock index 6b0ee6cac..103511e98 100644 --- a/client/dependencies.lock +++ b/client/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -9,10 +9,10 @@ "locked": "1.11.86" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.netflix.conductor:conductor-common": { "project": true @@ -36,19 +36,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -68,31 +68,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -135,35 +135,35 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -177,10 +177,10 @@ "locked": "1.11.86" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.netflix.conductor:conductor-common": { "project": true @@ -207,22 +207,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -240,16 +240,16 @@ "locked": "1.7.36" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -260,31 +260,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -330,38 +330,38 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -379,16 +379,16 @@ "locked": "1.7.36" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/common/dependencies.lock b/common/dependencies.lock index 83f5cb299..b8ea32112 100644 --- a/common/dependencies.lock +++ b/common/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "annotationsProcessorCodegen": { @@ -21,7 +21,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -38,60 +38,60 @@ ], "locked": "1.13.0" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-annotations-processor" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -103,42 +103,42 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springdoc:springdoc-openapi-ui": { "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -153,45 +153,45 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -206,45 +206,45 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "project": true @@ -262,43 +262,43 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java index ffcb71945..fefce75ba 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java @@ -12,13 +12,12 @@ */ package com.netflix.conductor.common.config; -import javax.annotation.PostConstruct; - import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; +import jakarta.annotation.PostConstruct; @Configuration public class ObjectMapperConfiguration { diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java index 3bd402013..6f3015e6a 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java @@ -17,13 +17,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - import org.apache.commons.lang3.StringUtils; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.PARAMETER; diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java index 55347529d..b010b4bb3 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java @@ -17,13 +17,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - import org.apache.commons.lang3.StringUtils; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.TYPE; diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java index 24f0ff433..f9dbc4c79 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java @@ -19,17 +19,17 @@ import java.util.HashMap; import java.util.List; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - import org.apache.commons.lang3.mutable.MutableBoolean; import com.netflix.conductor.common.metadata.workflow.WorkflowDef; import com.netflix.conductor.common.metadata.workflow.WorkflowTask; import com.netflix.conductor.common.utils.ConstraintParamUtil; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static java.lang.annotation.ElementType.TYPE; /** diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java index 56525c7b5..a498dca65 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java @@ -17,13 +17,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - import com.netflix.conductor.common.metadata.tasks.TaskDef; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static java.lang.annotation.ElementType.TYPE; /** diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java index 77dda4c1e..24084f2de 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java @@ -17,16 +17,15 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import com.netflix.conductor.annotations.protogen.ProtoEnum; import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; import com.google.protobuf.Any; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; /** Defines an event handler */ @ProtoMessage diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java index 59e119a0e..658079e0a 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java @@ -18,12 +18,6 @@ import java.util.Map; import java.util.Objects; -import javax.validation.Valid; -import javax.validation.constraints.Email; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import com.netflix.conductor.annotations.protogen.ProtoEnum; import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; @@ -31,6 +25,12 @@ import com.netflix.conductor.common.constraints.TaskTimeoutConstraint; import com.netflix.conductor.common.metadata.BaseDef; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + @ProtoMessage @TaskTimeoutConstraint @Valid diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java index 8953d499b..f31b6481b 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java @@ -17,8 +17,6 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -import javax.validation.constraints.NotEmpty; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.annotations.protogen.ProtoEnum; @@ -27,6 +25,7 @@ import com.google.protobuf.Any; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.validation.constraints.NotEmpty; /** Result of the task execution. */ @ProtoMessage diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java index cc01bca1a..e44edca62 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java @@ -15,14 +15,14 @@ import java.util.HashMap; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; - import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + @ProtoMessage public class StartWorkflowRequest { diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java index 816981b86..9cb934b64 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java @@ -15,14 +15,13 @@ import java.util.Map; import java.util.Objects; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonSetter; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; @ProtoMessage public class SubWorkflowParams { diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java index 6ce981f90..6d0676026 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java @@ -19,13 +19,6 @@ import java.util.Map; import java.util.Objects; -import javax.validation.Valid; -import javax.validation.constraints.Email; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import com.netflix.conductor.annotations.protogen.ProtoEnum; import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; @@ -35,6 +28,13 @@ import com.netflix.conductor.common.metadata.BaseDef; import com.netflix.conductor.common.metadata.tasks.TaskType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + @ProtoMessage @TaskReferenceNameUniqueConstraint public class WorkflowDef extends BaseDef { diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java index fc0eca86d..bf22c0265 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java @@ -14,12 +14,12 @@ import java.util.Objects; -import javax.validation.constraints.NotEmpty; - import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; import com.netflix.conductor.common.constraints.NoSemiColonConstraint; +import jakarta.validation.constraints.NotEmpty; + @ProtoMessage public class WorkflowDefSummary implements Comparable { diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java index 492a61d33..6395596f8 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -21,16 +21,15 @@ import java.util.Map; import java.util.Objects; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.PositiveOrZero; - import com.netflix.conductor.annotations.protogen.ProtoField; import com.netflix.conductor.annotations.protogen.ProtoMessage; import com.netflix.conductor.common.metadata.tasks.TaskDef; import com.netflix.conductor.common.metadata.tasks.TaskType; import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.PositiveOrZero; /** * This is the task definition definied as part of the {@link WorkflowDef}. The tasks definied in diff --git a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java index 6936f9535..b84c99775 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java +++ b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java @@ -15,9 +15,6 @@ import java.util.*; import java.util.stream.Collectors; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.annotations.protogen.ProtoEnum; @@ -27,6 +24,9 @@ import com.netflix.conductor.common.metadata.tasks.Task; import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; + @ProtoMessage public class Workflow extends Auditable { diff --git a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java index 76127124e..60502402f 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java @@ -14,8 +14,6 @@ import java.util.Map; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -25,6 +23,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; @Component public class SummaryUtil { diff --git a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java index a6a1f5cd1..36cf22c50 100644 --- a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java +++ b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java @@ -16,15 +16,15 @@ import java.util.List; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - import org.junit.Test; import com.netflix.conductor.common.metadata.events.EventHandler; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java index 41f966779..7e5108b05 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java @@ -16,16 +16,16 @@ import java.util.List; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - import org.junit.Before; import org.junit.Test; import com.netflix.conductor.common.metadata.tasks.TaskDef; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java index 1859c4a0d..bda8e0ddb 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java @@ -18,11 +18,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +32,10 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java index 16a08851e..2ed545418 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java @@ -18,11 +18,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - import org.junit.Before; import org.junit.Test; @@ -30,6 +25,11 @@ import com.netflix.conductor.common.metadata.workflow.WorkflowDef; import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java index 6d052e4d3..0d9ea921e 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java @@ -16,16 +16,16 @@ import java.util.List; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - import org.junit.Test; import com.netflix.conductor.common.metadata.tasks.TaskType; import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; diff --git a/core/build.gradle b/core/build.gradle index 7a56014e8..82881e7a8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -55,7 +55,7 @@ dependencies { testImplementation 'org.springframework.retry:spring-retry' testImplementation project(':conductor-common').sourceSets.test.output - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" testImplementation "org.junit.vintage:junit-vintage-engine" diff --git a/core/dependencies.lock b/core/dependencies.lock index 5699af6af..cef256a97 100644 --- a/core/dependencies.lock +++ b/core/dependencies.lock @@ -1,24 +1,24 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "locked": "2.4.0" @@ -51,28 +51,28 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" @@ -80,25 +80,25 @@ }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -107,7 +107,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "locked": "2.4.0" @@ -156,35 +156,35 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -192,19 +192,19 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "locked": "2.4.0" @@ -240,22 +240,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "2.3.3" @@ -267,19 +267,19 @@ "locked": "15.4" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" @@ -287,25 +287,25 @@ }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -314,7 +314,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "locked": "2.4.0" @@ -361,43 +361,8 @@ ], "locked": "3.12.0" }, - "org.apache.logging.log4j:log4j-api": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", - "com.netflix.conductor:conductor-common" - ], - "locked": "2.17.2" - }, - "org.apache.logging.log4j:log4j-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", - "com.netflix.conductor:conductor-common" - ], - "locked": "2.17.2" - }, - "org.apache.logging.log4j:log4j-jul": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", - "com.netflix.conductor:conductor-common" - ], - "locked": "2.17.2" - }, - "org.apache.logging.log4j:log4j-slf4j-impl": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", - "com.netflix.conductor:conductor-common" - ], - "locked": "2.17.2" - }, - "org.apache.logging.log4j:log4j-web": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", - "com.netflix.conductor:conductor-common" - ], - "locked": "2.17.2" - }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "2.3.3" @@ -409,19 +374,19 @@ "locked": "15.4" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" diff --git a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java index d88150005..92255bc4d 100644 --- a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java +++ b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java @@ -21,8 +21,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import javax.annotation.PreDestroy; - import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,6 +50,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PreDestroy; import static com.netflix.conductor.core.utils.Utils.DECIDER_QUEUE; diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java index 76edb8a68..1096fdf50 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java @@ -15,8 +15,6 @@ import java.util.HashMap; import java.util.Map; -import javax.validation.Validator; - import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +29,7 @@ import com.netflix.conductor.model.WorkflowModel; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.validation.Validator; import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_START_WORKFLOW; import static com.netflix.conductor.model.TaskModel.Status.COMPLETED; diff --git a/core/src/main/java/com/netflix/conductor/service/AdminService.java b/core/src/main/java/com/netflix/conductor/service/AdminService.java index 84d68c279..474743db4 100644 --- a/core/src/main/java/com/netflix/conductor/service/AdminService.java +++ b/core/src/main/java/com/netflix/conductor/service/AdminService.java @@ -15,12 +15,12 @@ import java.util.List; import java.util.Map; -import javax.validation.constraints.NotEmpty; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.metadata.tasks.Task; +import jakarta.validation.constraints.NotEmpty; + @Validated public interface AdminService { diff --git a/core/src/main/java/com/netflix/conductor/service/EventService.java b/core/src/main/java/com/netflix/conductor/service/EventService.java index c2f29e734..397503cf4 100644 --- a/core/src/main/java/com/netflix/conductor/service/EventService.java +++ b/core/src/main/java/com/netflix/conductor/service/EventService.java @@ -14,14 +14,14 @@ import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.metadata.events.EventHandler; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + @Validated public interface EventService { diff --git a/core/src/main/java/com/netflix/conductor/service/MetadataService.java b/core/src/main/java/com/netflix/conductor/service/MetadataService.java index 701055ef8..babd46627 100644 --- a/core/src/main/java/com/netflix/conductor/service/MetadataService.java +++ b/core/src/main/java/com/netflix/conductor/service/MetadataService.java @@ -16,11 +16,6 @@ import java.util.Map; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.metadata.events.EventHandler; @@ -29,6 +24,11 @@ import com.netflix.conductor.common.metadata.workflow.WorkflowDefSummary; import com.netflix.conductor.common.model.BulkResponse; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + @Validated public interface MetadataService { diff --git a/core/src/main/java/com/netflix/conductor/service/TaskService.java b/core/src/main/java/com/netflix/conductor/service/TaskService.java index 7f4f3d0a6..d89772659 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskService.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskService.java @@ -15,10 +15,6 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.metadata.tasks.PollData; @@ -29,6 +25,10 @@ import com.netflix.conductor.common.run.SearchResult; import com.netflix.conductor.common.run.TaskSummary; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + @Validated public interface TaskService { diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java index 2c1ef0f7f..e54c94f60 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java @@ -14,13 +14,13 @@ import java.util.List; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Size; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.model.BulkResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; + @Validated public interface WorkflowBulkService { diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java index bb760ac68..6ba2d7e64 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java @@ -15,12 +15,6 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import org.springframework.validation.annotation.Validated; import com.netflix.conductor.common.metadata.workflow.RerunWorkflowRequest; @@ -32,6 +26,12 @@ import com.netflix.conductor.common.run.Workflow; import com.netflix.conductor.common.run.WorkflowSummary; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + @Validated public interface WorkflowService { diff --git a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java index 888dd7a49..3a6400f6b 100644 --- a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java +++ b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java @@ -20,11 +20,6 @@ import java.time.format.DateTimeParseException; import java.util.Optional; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.common.metadata.tasks.TaskDef; @@ -32,6 +27,11 @@ import com.netflix.conductor.common.metadata.workflow.WorkflowTask; import com.netflix.conductor.core.utils.DateTimeUtils; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static com.netflix.conductor.core.execution.tasks.Terminate.getTerminationStatusParameter; import static com.netflix.conductor.core.execution.tasks.Terminate.validateInputStatus; import static com.netflix.conductor.core.execution.tasks.Wait.DURATION_INPUT; diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy index 3673c0091..1dd2eeec1 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy @@ -14,8 +14,6 @@ package com.netflix.conductor.core.execution.tasks import java.time.Duration -import org.junit.Test - import com.netflix.conductor.common.metadata.tasks.TaskDef import com.netflix.conductor.service.MetadataService @@ -45,7 +43,6 @@ class IsolatedTaskQueueProducerSpec extends Specification { Duration.ofSeconds(10)) } - @Test def "addTaskQueuesAddsElementToQueue"() { given: TaskDef taskDef = new TaskDef(isolationGroupId: "isolated") diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy index 3e64d7f23..9c6af719d 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy @@ -12,9 +12,6 @@ */ package com.netflix.conductor.core.execution.tasks -import javax.validation.ConstraintViolation -import javax.validation.Validator - import com.netflix.conductor.common.config.ObjectMapperProvider import com.netflix.conductor.core.exception.NotFoundException import com.netflix.conductor.core.exception.TransientException @@ -23,6 +20,8 @@ import com.netflix.conductor.core.operation.StartWorkflowOperation import com.netflix.conductor.model.TaskModel import com.netflix.conductor.model.WorkflowModel +import jakarta.validation.ConstraintViolation +import jakarta.validation.Validator import spock.lang.Specification import spock.lang.Subject diff --git a/core/src/test/java/com/netflix/conductor/TestUtils.java b/core/src/test/java/com/netflix/conductor/TestUtils.java index 41f1377e4..08428322c 100644 --- a/core/src/test/java/com/netflix/conductor/TestUtils.java +++ b/core/src/test/java/com/netflix/conductor/TestUtils.java @@ -16,7 +16,7 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolation; public class TestUtils { diff --git a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java index 55cd1c7d8..e431a16e6 100644 --- a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java @@ -16,8 +16,6 @@ import java.util.Optional; import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -37,6 +35,8 @@ import com.netflix.conductor.core.exception.TerminateWorkflowException; import com.netflix.conductor.dao.MetadataDAO; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java index 620bb4e30..86a61e6ad 100644 --- a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java @@ -14,8 +14,6 @@ import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -26,6 +24,8 @@ import com.netflix.conductor.core.events.EventQueues; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java index 8ea351ac7..1c629c4ec 100644 --- a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java @@ -20,8 +20,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -40,6 +38,8 @@ import com.netflix.conductor.dao.EventHandlerDAO; import com.netflix.conductor.dao.MetadataDAO; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java index 2c54d3a31..240b99e8c 100644 --- a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java @@ -15,8 +15,6 @@ import java.util.List; import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +29,8 @@ import com.netflix.conductor.common.run.TaskSummary; import com.netflix.conductor.dao.QueueDAO; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.*; diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java index 20dcbfd4f..1936401f1 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java @@ -17,8 +17,6 @@ import java.util.List; import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +27,8 @@ import com.netflix.conductor.core.execution.WorkflowExecutor; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java index dd6108983..772f2f2e9 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java @@ -18,8 +18,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolationException; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +36,8 @@ import com.netflix.conductor.core.execution.WorkflowExecutor; import com.netflix.conductor.core.operation.StartWorkflowOperation; +import jakarta.validation.ConstraintViolationException; + import static com.netflix.conductor.TestUtils.getConstraintViolationMessages; import static org.junit.Assert.*; diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java index 6e6f5d3b2..c66ed56df 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java @@ -18,12 +18,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; - -import org.apache.bval.jsr.ApacheValidationProvider; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -36,6 +30,11 @@ import com.netflix.conductor.common.metadata.workflow.WorkflowTask; import com.netflix.conductor.dao.MetadataDAO; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; @@ -49,10 +48,7 @@ public class WorkflowDefConstraintTest { @BeforeClass public static void init() { - validatorFactory = - Validation.byProvider(ApacheValidationProvider.class) - .configure() - .buildValidatorFactory(); + validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); } diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java index a1105ed72..520dd6b17 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java @@ -20,13 +20,6 @@ import java.util.Map; import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.executable.ExecutableValidator; - -import org.apache.bval.jsr.ApacheValidationProvider; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; @@ -41,6 +34,12 @@ import com.netflix.conductor.core.execution.tasks.Terminate; import com.netflix.conductor.dao.MetadataDAO; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import jakarta.validation.executable.ExecutableValidator; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; @@ -54,10 +53,7 @@ public class WorkflowTaskTypeConstraintTest { @BeforeClass public static void init() { - validatorFactory = - Validation.byProvider(ApacheValidationProvider.class) - .configure() - .buildValidatorFactory(); + validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); } diff --git a/dependencies.gradle b/dependencies.gradle index 547b4f1ae..2a9d9f64f 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -15,7 +15,8 @@ * Common place to define all the version dependencies */ ext { - revActivation = '2.0.0' + revActivation = '3.0.2' + revApacheHttpComponentsClient5 = '5.2.1' revAwaitility = '3.1.6' revAwsSdk = '1.11.86' revBval = '2.0.5' @@ -26,12 +27,14 @@ ext { revElasticSearch6 = '6.8.12' revEmbeddedRedis = '0.6' revEurekaClient = '1.10.10' - revGroovy = '2.5.22' - revGrpc = '1.+' + revGroovy = '4.0.9' + revGrpc = '1.57.2' revGuava = '30.0-jre' revHamcrestAllMatchers = '1.8' revHealth = '1.1.+' - revJAXB = '2.3.3' + revProtoBuf = '3.21.12' + revJakartaAnnotation = '2.1.1' + revJAXB = '4.0.1' revJAXRS = '2.1.1' revJedis = '3.3.0' revJersey = '1.19.4' @@ -43,14 +46,14 @@ ext { revOpenapi = '1.6.+' revOrkesQueues = '1.0.3' revPowerMock = '2.0.9' - revProtoBuf = '3.21.7' + revProtoBuf = '3.21.12' revProtogenAnnotations = '1.0.0' revProtogenCodegen = '1.4.0' revRarefiedRedis = '0.0.17' revRedisson = '3.13.3' revRxJava = '1.2.2' revSpectator = '0.122.0' - revSpock = '1.3-groovy-2.5' + revSpock = '2.4-M1-groovy-4.0' revSpotifyCompletableFutures = '0.3.3' revTestContainer = '1.15.3' } diff --git a/dependencies.lock b/dependencies.lock index b2d91aea1..752d96aac 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,24 +1,24 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "jacocoAgent": { @@ -33,19 +33,19 @@ }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "testCompileClasspath": { @@ -53,28 +53,28 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -82,28 +82,28 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/es6-persistence/build.gradle b/es6-persistence/build.gradle index 2bf0d49c8..d033895bb 100644 --- a/es6-persistence/build.gradle +++ b/es6-persistence/build.gradle @@ -23,9 +23,9 @@ dependencies { // SBMTODO: remove guava dep implementation "com.google.guava:guava:${revGuava}" - implementation "org.elasticsearch.client:transport" - implementation "org.elasticsearch.client:elasticsearch-rest-client" - implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client" + implementation "org.elasticsearch.client:transport:${revElasticSearch6}" + implementation "org.elasticsearch.client:elasticsearch-rest-client:${revElasticSearch6}" + implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:${revElasticSearch6}" testImplementation 'org.springframework.retry:spring-retry' testImplementation "org.awaitility:awaitility:${revAwaitility}" diff --git a/es6-persistence/dependencies.lock b/es6-persistence/dependencies.lock index 9fb73e7bc..32aa282aa 100644 --- a/es6-persistence/dependencies.lock +++ b/es6-persistence/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -21,19 +21,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "locked": "6.8.12" @@ -45,7 +45,7 @@ "locked": "6.8.12" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" @@ -56,27 +56,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -92,7 +92,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -171,7 +171,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -179,7 +179,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -187,7 +187,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -195,7 +195,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -203,7 +203,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "locked": "6.8.12" @@ -241,19 +241,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" @@ -271,10 +271,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" @@ -288,27 +288,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -324,7 +324,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -406,7 +406,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -414,7 +414,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -422,7 +422,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -430,7 +430,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -438,7 +438,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" @@ -462,10 +462,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java index 0a8c86f35..bbf05423e 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java @@ -21,9 +21,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.apache.commons.lang3.StringUtils; import org.elasticsearch.ResourceAlreadyExistsException; import org.elasticsearch.action.DocWriteResponse; @@ -73,6 +70,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.MapType; import com.fasterxml.jackson.databind.type.TypeFactory; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; @Trace public class ElasticSearchDAOV6 extends ElasticSearchBaseDAO implements IndexDAO { diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java index 9792d5222..e51d13341 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java @@ -22,9 +22,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; @@ -79,6 +76,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.type.MapType; import com.fasterxml.jackson.databind.type.TypeFactory; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; @Trace public class ElasticSearchRestDAOV6 extends ElasticSearchBaseDAO implements IndexDAO { diff --git a/grpc-client/build.gradle b/grpc-client/build.gradle index 4ddd6bc71..d7e6d7239 100644 --- a/grpc-client/build.gradle +++ b/grpc-client/build.gradle @@ -21,5 +21,6 @@ dependencies { implementation "com.google.protobuf:protobuf-java:${revProtoBuf}" implementation "org.slf4j:slf4j-api" implementation "org.apache.commons:commons-lang3" + implementation "jakarta.annotation:jakarta.annotation-api:${revJakartaAnnotation}" implementation "com.google.guava:guava:${revGuava}" } diff --git a/grpc-client/dependencies.lock b/grpc-client/dependencies.lock index 95623d88c..9876ffb92 100644 --- a/grpc-client/dependencies.lock +++ b/grpc-client/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -9,7 +9,7 @@ "locked": "30.0-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true @@ -18,34 +18,34 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" }, "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" } }, "runtimeClasspath": { @@ -53,19 +53,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -75,7 +75,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -93,25 +93,22 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, - "javax.annotation:javax.annotation-api": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-grpc" - ], - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -131,7 +128,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -139,7 +136,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -147,7 +144,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -155,7 +152,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -163,10 +160,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" } }, "testCompileClasspath": { @@ -174,7 +171,7 @@ "locked": "30.0-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true @@ -183,13 +180,13 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" }, "junit:junit": { "locked": "4.13.2" @@ -198,31 +195,31 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -230,19 +227,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -252,7 +249,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -270,25 +267,22 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, - "javax.annotation:javax.annotation-api": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-grpc" - ], - "locked": "1.3.2" + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "junit:junit": { "locked": "4.13.2" @@ -311,7 +305,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -319,7 +313,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -327,7 +321,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -335,7 +329,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -343,19 +337,19 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java index 5b299a93b..0450547c5 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java @@ -14,8 +14,6 @@ import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +22,7 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import jakarta.annotation.Nullable; abstract class ClientBase { diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java index 256f33b10..83e51cd64 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java @@ -14,8 +14,6 @@ import java.util.List; -import javax.annotation.Nullable; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.common.metadata.tasks.TaskDef; @@ -25,6 +23,7 @@ import com.google.common.base.Preconditions; import io.grpc.ManagedChannelBuilder; +import jakarta.annotation.Nullable; public class MetadataClient extends ClientBase { diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java index 92403d8a4..0cbf37c99 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java @@ -16,8 +16,6 @@ import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.common.metadata.tasks.Task; @@ -34,6 +32,7 @@ import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import io.grpc.ManagedChannelBuilder; +import jakarta.annotation.Nullable; public class TaskClient extends ClientBase { diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java index fd211f0af..86c78e682 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java @@ -16,8 +16,6 @@ import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; - import org.apache.commons.lang3.StringUtils; import com.netflix.conductor.common.metadata.workflow.RerunWorkflowRequest; @@ -32,6 +30,7 @@ import com.google.common.base.Preconditions; import io.grpc.ManagedChannelBuilder; +import jakarta.annotation.Nullable; public class WorkflowClient extends ClientBase { diff --git a/grpc-server/dependencies.lock b/grpc-server/dependencies.lock index 6dcec7164..144391fc1 100644 --- a/grpc-server/dependencies.lock +++ b/grpc-server/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -15,31 +15,31 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { - "locked": "1.57.1" + "locked": "1.57.2" }, "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -47,33 +47,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -81,13 +81,13 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.22.3" + "locked": "3.24.0" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -127,46 +127,46 @@ "locked": "2.7" }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -189,7 +189,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -198,7 +198,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -207,7 +207,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -216,7 +216,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -225,7 +225,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -245,13 +245,13 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-testing": { - "locked": "1.57.1" + "locked": "1.57.2" }, "junit:junit": { "locked": "4.13.2" @@ -260,28 +260,28 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testinfected.hamcrest-matchers:all-matchers": { "locked": "1.8" @@ -292,33 +292,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -326,13 +326,13 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.22.3" + "locked": "3.24.0" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -372,49 +372,49 @@ "locked": "2.7" }, "io.grpc:grpc-netty": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-testing": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "junit:junit": { "locked": "4.13.2" @@ -440,7 +440,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -449,7 +449,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -458,7 +458,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -476,10 +476,10 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -488,10 +488,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testinfected.hamcrest-matchers:all-matchers": { "locked": "1.8" diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java index 7d10ac59f..4f62be3d8 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java @@ -15,15 +15,14 @@ import java.io.IOException; import java.util.List; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.grpc.BindableService; import io.grpc.Server; import io.grpc.ServerBuilder; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; public class GRPCServer { diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java index 0dd626fa6..4b0cae8bb 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java @@ -14,8 +14,6 @@ import java.util.Arrays; -import javax.annotation.Nonnull; - import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; @@ -25,6 +23,7 @@ import io.grpc.StatusException; import io.grpc.protobuf.lite.ProtoLiteUtils; import io.grpc.stub.StreamObserver; +import jakarta.annotation.Nonnull; public class GRPCHelper { diff --git a/grpc/build.gradle b/grpc/build.gradle index 6286e2a0f..c75b2f7e7 100644 --- a/grpc/build.gradle +++ b/grpc/build.gradle @@ -33,19 +33,21 @@ dependencies { implementation "com.google.protobuf:protobuf-java:${revProtoBuf}" implementation "io.grpc:grpc-protobuf:${revGrpc}" implementation "io.grpc:grpc-stub:${revGrpc}" - implementation "javax.annotation:javax.annotation-api:1.3.2" + implementation "jakarta.annotation:jakarta.annotation-api:${revJakartaAnnotation}" + implementation "javax.annotation:javax.annotation-api:1.3.2" //Needs to be added as a workaround for the generated tags + } def artifactName = 'com.google.protobuf:protoc:3.14.0:osx-x86_64' switch (org.gradle.internal.os.OperatingSystem.current()) { case org.gradle.internal.os.OperatingSystem.LINUX: - artifactName = "com.google.protobuf:protoc:3.21.7" + artifactName = "com.google.protobuf:protoc:3.21.12" break; case org.gradle.internal.os.OperatingSystem.MAC_OS: artifactName = "com.google.protobuf:protoc:3.14.0:osx-x86_64" break; case org.gradle.internal.os.OperatingSystem.WINDOWS: - artifactName = "com.google.protobuf:protoc:3.21.7" + artifactName = "com.google.protobuf:protoc:3.21.12" break; } diff --git a/grpc/dependencies.lock b/grpc/dependencies.lock index 82071c38c..c36eb50e6 100644 --- a/grpc/dependencies.lock +++ b/grpc/dependencies.lock @@ -1,39 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" } }, "compileProtoPath": { @@ -41,25 +44,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -71,10 +74,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -96,40 +102,40 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "protobufToolsLocator_grpc": { "io.grpc:protoc-gen-grpc-java": { - "locked": "1.57.1" + "locked": "1.57.2" } }, "protobufToolsLocator_protoc": { @@ -142,25 +148,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -172,10 +178,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -197,49 +206,52 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" } }, "testCompileClasspath": { "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -248,28 +260,28 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testCompileProtoPath": { @@ -277,25 +289,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -307,10 +319,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -335,44 +350,44 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -380,25 +395,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -410,10 +425,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -438,44 +456,44 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java b/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java index c9cd06e38..8548d9120 100644 --- a/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java +++ b/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java @@ -40,6 +40,7 @@ import com.netflix.conductor.proto.WorkflowPb; import com.netflix.conductor.proto.WorkflowSummaryPb; import com.netflix.conductor.proto.WorkflowTaskPb; +import jakarta.annotation.Generated; import java.lang.IllegalArgumentException; import java.lang.Object; import java.lang.String; @@ -49,7 +50,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Generated; @Generated("com.netflix.conductor.annotationsprocessor.protogen") public abstract class AbstractProtoMapper { diff --git a/grpc/src/test/java/com/netflix/conductor/grpc/TestProtoMapper.java b/grpc/src/test/java/com/netflix/conductor/grpc/TestProtoMapper.java index 31286609a..80d9f08e0 100644 --- a/grpc/src/test/java/com/netflix/conductor/grpc/TestProtoMapper.java +++ b/grpc/src/test/java/com/netflix/conductor/grpc/TestProtoMapper.java @@ -39,7 +39,7 @@ public void workflowTaskFromProto() { final WorkflowTaskPb.WorkflowTask taskWithDefaultRetryCount = WorkflowTaskPb.WorkflowTask.newBuilder().build(); final WorkflowTaskPb.WorkflowTask taskWith1RetryCount = WorkflowTaskPb.WorkflowTask.newBuilder().setRetryCount(1).build(); final WorkflowTaskPb.WorkflowTask taskWithNoRetryCount = WorkflowTaskPb.WorkflowTask.newBuilder().setRetryCount(-1).build(); - assertEquals(new Integer(0), mapper.fromProto(taskWithDefaultRetryCount).getRetryCount()); + assertEquals(Integer.valueOf(0), mapper.fromProto(taskWithDefaultRetryCount).getRetryCount()); assertEquals(1, mapper.fromProto(taskWith1RetryCount).getRetryCount().intValue()); assertNull(mapper.fromProto(taskWithNoRetryCount).getRetryCount()); } diff --git a/http-task/build.gradle b/http-task/build.gradle index a9f76899e..93bbe2599 100644 --- a/http-task/build.gradle +++ b/http-task/build.gradle @@ -17,6 +17,7 @@ dependencies { compileOnly 'org.springframework.boot:spring-boot-starter-web' implementation "javax.ws.rs:jsr311-api:${revJsr311Api}" + implementation("org.apache.httpcomponents.client5:httpclient5:${revApacheHttpComponentsClient5}") testImplementation 'org.springframework.boot:spring-boot-starter-web' testImplementation "org.testcontainers:mockserver:${revTestContainer}" diff --git a/http-task/dependencies.lock b/http-task/dependencies.lock index f6669d5c9..9004cb56d 100644 --- a/http-task/dependencies.lock +++ b/http-task/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -14,26 +14,29 @@ "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -41,46 +44,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -119,23 +122,26 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", @@ -156,7 +162,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +170,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +178,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +186,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +194,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -207,23 +213,26 @@ "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "junit:junit": { "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.bouncycastle:bcpkix-jdk15on": { "locked": "1.70" @@ -232,19 +241,19 @@ "locked": "1.70" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.mock-server:mockserver-client-java": { "locked": "5.12.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testcontainers:mockserver": { "locked": "1.15.3" @@ -255,46 +264,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -333,23 +342,26 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "junit:junit": { "locked": "4.13.2" }, @@ -373,7 +385,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -381,7 +393,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -389,7 +401,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -397,7 +409,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -405,7 +417,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.bouncycastle:bcpkix-jdk15on": { "locked": "1.70" @@ -414,7 +426,7 @@ "locked": "1.70" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.mock-server:mockserver-client-java": { "locked": "5.12.0" @@ -426,13 +438,13 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.testcontainers:mockserver": { "locked": "1.15.3" diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java index 3b3a0c14f..d388b165e 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java @@ -167,13 +167,18 @@ protected HttpResponse httpCall(Input input) throws Exception { HttpResponse response = new HttpResponse(); try { ResponseEntity responseEntity = - restTemplate.exchange(input.getUri(), input.getMethod(), request, String.class); + restTemplate.exchange( + input.getUri(), + HttpMethod.valueOf(input.getMethod()), + request, + String.class); if (responseEntity.getStatusCode().is2xxSuccessful() && responseEntity.hasBody()) { response.body = extractBody(responseEntity.getBody()); } response.statusCode = responseEntity.getStatusCodeValue(); - response.reasonPhrase = responseEntity.getStatusCode().getReasonPhrase(); + response.reasonPhrase = + HttpStatus.valueOf(responseEntity.getStatusCode().value()).getReasonPhrase(); response.headers = responseEntity.getHeaders(); return response; } catch (RestClientException ex) { @@ -253,7 +258,7 @@ public Map asMap() { public static class Input { - private HttpMethod method; // PUT, POST, GET, DELETE, OPTIONS, HEAD + private String method; // PUT, POST, GET, DELETE, OPTIONS, HEAD private String vipAddress; private String appName; private Map headers = new HashMap<>(); @@ -267,7 +272,7 @@ public static class Input { /** * @return the method */ - public HttpMethod getMethod() { + public String getMethod() { return method; } @@ -275,7 +280,7 @@ public HttpMethod getMethod() { * @param method the method to set */ public void setMethod(String method) { - this.method = HttpMethod.valueOf(method); + this.method = method; } /** diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java index d460e36f6..0a1cfd2f7 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java @@ -14,7 +14,10 @@ import java.time.Duration; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import org.apache.hc.core5.http.io.SocketConfig; +import org.apache.hc.core5.util.Timeout; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -32,7 +35,7 @@ @Component public class DefaultRestTemplateProvider implements RestTemplateProvider { - private final ThreadLocal threadLocalRestTemplate; + private final ThreadLocal threadLocalRestTemplateBuilder; private final int defaultReadTimeout; private final int defaultConnectTimeout; @@ -41,20 +44,28 @@ public class DefaultRestTemplateProvider implements RestTemplateProvider { public DefaultRestTemplateProvider( @Value("${conductor.tasks.http.readTimeout:150ms}") Duration readTimeout, @Value("${conductor.tasks.http.connectTimeout:100ms}") Duration connectTimeout) { - this.threadLocalRestTemplate = ThreadLocal.withInitial(RestTemplate::new); + this.threadLocalRestTemplateBuilder = ThreadLocal.withInitial(RestTemplateBuilder::new); this.defaultReadTimeout = (int) readTimeout.toMillis(); this.defaultConnectTimeout = (int) connectTimeout.toMillis(); } @Override public @NonNull RestTemplate getRestTemplate(@NonNull HttpTask.Input input) { - RestTemplate restTemplate = threadLocalRestTemplate.get(); + Duration timeout = + Duration.ofMillis( + Optional.ofNullable(input.getReadTimeOut()).orElse(defaultReadTimeout)); + threadLocalRestTemplateBuilder.get().setReadTimeout(timeout); + RestTemplate restTemplate = + threadLocalRestTemplateBuilder.get().setReadTimeout(timeout).build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); + SocketConfig.Builder builder = SocketConfig.custom(); + builder.setSoTimeout( + Timeout.of( + Optional.ofNullable(input.getReadTimeOut()).orElse(defaultReadTimeout), + TimeUnit.MILLISECONDS)); requestFactory.setConnectTimeout( Optional.ofNullable(input.getConnectionTimeOut()).orElse(defaultConnectTimeout)); - requestFactory.setReadTimeout( - Optional.ofNullable(input.getReadTimeOut()).orElse(defaultReadTimeout)); restTemplate.setRequestFactory(requestFactory); return restTemplate; } diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java index f82662556..2b2f6a6df 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java @@ -315,7 +315,7 @@ public void testHTTPGETReadTimeOut() { task.setScheduledTime(0); httpTask.start(workflow, task, workflowExecutor); - assertEquals(task.getStatus(), TaskModel.Status.FAILED); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); } @Test diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java index 816f5d6f9..3141e47e5 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java @@ -14,6 +14,7 @@ import java.time.Duration; +import org.junit.Ignore; import org.junit.Test; import org.springframework.web.client.RestTemplate; @@ -46,6 +47,7 @@ public void differentObjectsForDifferentThreads() throws InterruptedException { } @Test + @Ignore("We can no longer do this and have customizable timeouts per HttpTask.") public void sameObjectForSameThread() { DefaultRestTemplateProvider defaultRestTemplateProvider = new DefaultRestTemplateProvider(Duration.ofMillis(150), Duration.ofMillis(100)); diff --git a/java-sdk/build.gradle b/java-sdk/build.gradle index 6572905d2..0f9a211f9 100644 --- a/java-sdk/build.gradle +++ b/java-sdk/build.gradle @@ -20,7 +20,7 @@ dependencies { testImplementation "com.fasterxml.jackson.core:jackson-core" testImplementation "org.apache.commons:commons-lang3" - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" } diff --git a/java-sdk/dependencies.lock b/java-sdk/dependencies.lock index 17084fccf..273c00559 100644 --- a/java-sdk/dependencies.lock +++ b/java-sdk/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -9,7 +9,7 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -27,19 +27,19 @@ "locked": "2.1.1" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -62,31 +62,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -95,7 +95,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -161,7 +161,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -169,7 +169,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -177,7 +177,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -185,7 +185,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -193,7 +193,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -216,10 +216,10 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -243,22 +243,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -270,19 +270,19 @@ "locked": "15.4" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework:spring-web": { - "locked": "5.3.22" + "locked": "6.0.12" } }, "testRuntimeClasspath": { @@ -299,31 +299,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -332,7 +332,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -401,7 +401,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -409,7 +409,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -417,7 +417,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -425,7 +425,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -433,10 +433,10 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -457,19 +457,19 @@ "locked": "1.7.36" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework:spring-web": { - "locked": "5.3.22" + "locked": "6.0.12" } } } \ No newline at end of file diff --git a/json-jq-task/dependencies.lock b/json-jq-task/dependencies.lock index 27e43fb55..010c2e434 100644 --- a/json-jq-task/dependencies.lock +++ b/json-jq-task/dependencies.lock @@ -1,12 +1,12 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.netflix.conductor:conductor-common": { "project": true @@ -18,22 +18,22 @@ "locked": "0.0.13" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -41,46 +41,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -119,19 +119,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "net.thisptr:jackson-jq": { "locked": "0.0.13" @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +188,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -199,7 +199,7 @@ }, "testCompileClasspath": { "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.netflix.conductor:conductor-common": { "project": true @@ -214,28 +214,28 @@ "locked": "0.0.13" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -243,46 +243,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -321,19 +321,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -361,7 +361,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -369,7 +369,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -377,7 +377,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -385,7 +385,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -393,10 +393,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -405,10 +405,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/redis-concurrency-limit/build.gradle b/redis-concurrency-limit/build.gradle index 9c826ea61..b0061578d 100644 --- a/redis-concurrency-limit/build.gradle +++ b/redis-concurrency-limit/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation "redis.clients:jedis:3.6.0" // Jedis version "revJedis=3.3.0" does not play well with Spring Data Redis implementation "org.apache.commons:commons-lang3" - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" testImplementation "org.testcontainers:spock:${revTestContainer}" diff --git a/redis-concurrency-limit/dependencies.lock b/redis-concurrency-limit/dependencies.lock index b7a19c231..936087215 100644 --- a/redis-concurrency-limit/dependencies.lock +++ b/redis-concurrency-limit/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -15,28 +15,28 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { "locked": "2.7.2" }, "redis.clients:jedis": { - "locked": "3.6.0" + "locked": "3.3.0" } }, "runtimeClasspath": { @@ -44,27 +44,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -77,7 +77,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,21 +188,15 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" - }, - "org.openjdk.nashorn:nashorn-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-core" - ], - "locked": "15.4" + "locked": "2.21.0" }, "redis.clients:jedis": { - "locked": "3.6.0" + "locked": "3.3.0" } }, "testCompileClasspath": { "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-common": { "project": true @@ -217,37 +211,37 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { "locked": "2.7.2" @@ -259,7 +253,7 @@ "locked": "1.15.3" }, "redis.clients:jedis": { - "locked": "3.6.0" + "locked": "3.3.0" } }, "testRuntimeClasspath": { @@ -267,27 +261,27 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -300,7 +294,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -382,7 +376,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -390,7 +384,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -398,7 +392,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -406,7 +400,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -414,10 +408,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" @@ -429,16 +423,16 @@ "locked": "15.4" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { "locked": "2.7.2" diff --git a/redis-lock/dependencies.lock b/redis-lock/dependencies.lock index fe504d621..82ba26589 100644 --- a/redis-lock/dependencies.lock +++ b/redis-lock/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -12,25 +12,25 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.redisson:redisson": { "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -38,46 +38,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -116,19 +116,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -150,7 +150,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -158,7 +158,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -166,7 +166,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -174,7 +174,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -182,7 +182,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -208,31 +208,31 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.redisson:redisson": { "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -240,33 +240,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.github.kstyrc:embedded-redis": { "locked": "0.6" @@ -276,13 +276,13 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -321,19 +321,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -358,7 +358,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -366,7 +366,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -374,7 +374,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -382,7 +382,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -390,10 +390,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -405,10 +405,10 @@ "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/redis-persistence/dependencies.lock b/redis-persistence/dependencies.lock index e5545e2aa..4da148cad 100644 --- a/redis-persistence/dependencies.lock +++ b/redis-persistence/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -18,25 +18,25 @@ "locked": "1.4.20" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.rarefiedredis.redis:redis-java": { "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "redis.clients:jedis": { "locked": "3.3.0" @@ -47,46 +47,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -131,19 +131,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -165,7 +165,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -173,7 +173,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -181,7 +181,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -189,7 +189,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -197,7 +197,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -229,31 +229,31 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.rarefiedredis.redis:redis-java": { "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "redis.clients:jedis": { "locked": "3.3.0" @@ -264,46 +264,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -348,19 +348,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -385,7 +385,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -393,7 +393,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -401,7 +401,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -409,7 +409,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -417,10 +417,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -432,10 +432,10 @@ "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "redis.clients:jedis": { "locked": "3.3.0" diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java index 9e32a1d5f..2ae22e7d0 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java @@ -40,6 +40,9 @@ public List getHosts() { } private List parseHostsFromConfig() { + System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + System.out.println("Properties in ConfigurationHostSupplier: "); + System.out.println(properties); String hosts = properties.getHosts(); if (hosts == null) { String message = diff --git a/rest/dependencies.lock b/rest/dependencies.lock index a3cfdea4b..77e50509f 100644 --- a/rest/dependencies.lock +++ b/rest/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -15,25 +15,25 @@ "locked": "1.1.4" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springdoc:springdoc-openapi-ui": { "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "runtimeClasspath": { @@ -41,46 +41,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -122,19 +122,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +188,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -200,7 +200,7 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testCompileClasspath": { @@ -217,34 +217,34 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springdoc:springdoc-openapi-ui": { "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "testRuntimeClasspath": { @@ -252,46 +252,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -333,19 +333,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -370,7 +370,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -378,7 +378,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -386,7 +386,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -394,7 +394,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -402,10 +402,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -417,13 +417,13 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" } } } \ No newline at end of file diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java index ab5c47eee..f61b8e95d 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java @@ -15,8 +15,6 @@ import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; @@ -33,6 +31,7 @@ import com.netflix.conductor.metrics.Monitors; import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import jakarta.servlet.http.HttpServletRequest; @RestControllerAdvice @Order(ValidationExceptionMapper.ORDER + 1) diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java index 704f72e05..928e7419f 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java @@ -16,11 +16,6 @@ import java.util.Arrays; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.ValidationException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; @@ -35,6 +30,11 @@ import com.netflix.conductor.core.utils.Utils; import com.netflix.conductor.metrics.Monitors; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.ValidationException; + /** This class converts Hibernate {@link ValidationException} into http response. */ @RestControllerAdvice @Order(ValidationExceptionMapper.ORDER) diff --git a/server/build.gradle b/server/build.gradle index 1bee56916..c103fd3a3 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-log4j2' implementation 'org.apache.logging.log4j:log4j-web' + implementation "redis.clients:jedis:${revJedis}" implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation "io.orkes.queues:orkes-conductor-queues:${revOrkesQueues}" diff --git a/server/dependencies.lock b/server/dependencies.lock index c19744790..cb77006ed 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { @@ -45,40 +45,43 @@ "locked": "1.0.3" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springdoc:springdoc-openapi-ui": { "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" + }, + "redis.clients:jedis": { + "locked": "3.3.0" } }, "productionRuntimeClasspath": { @@ -104,34 +107,34 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-json-jq-task" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -146,13 +149,13 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.22.3" + "locked": "3.24.0" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -273,25 +276,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -301,25 +304,25 @@ "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -372,7 +375,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -392,7 +395,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -412,7 +415,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -432,7 +435,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -452,13 +455,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ @@ -473,7 +476,7 @@ "locked": "6.8.12" }, "org.glassfish.jaxb:jaxb-runtime": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -500,32 +503,32 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "redis.clients:jedis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence" ], - "locked": "3.8.0" + "locked": "3.3.0" } }, "runtimeClasspath": { @@ -551,34 +554,34 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-json-jq-task" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -593,13 +596,13 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.22.3" + "locked": "3.24.0" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -720,25 +723,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -748,25 +751,25 @@ "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -819,7 +822,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -839,7 +842,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -859,7 +862,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -879,7 +882,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -899,13 +902,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ @@ -920,7 +923,7 @@ "locked": "6.8.12" }, "org.glassfish.jaxb:jaxb-runtime": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -947,37 +950,37 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "redis.clients:jedis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence" ], - "locked": "3.8.0" + "locked": "3.3.0" } }, "testCompileClasspath": { "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-awss3-storage": { "project": true @@ -1019,13 +1022,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-testing": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -1034,46 +1037,49 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springdoc:springdoc-openapi-ui": { "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" + }, + "redis.clients:jedis": { + "locked": "3.3.0" } }, "testRuntimeClasspath": { @@ -1099,34 +1105,34 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-json-jq-task" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -1141,13 +1147,13 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -1268,28 +1274,28 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-testing": { - "locked": "1.57.1" + "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -1299,25 +1305,25 @@ "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -1373,7 +1379,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -1393,7 +1399,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -1413,7 +1419,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -1433,7 +1439,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -1453,13 +1459,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ @@ -1474,10 +1480,10 @@ "locked": "6.8.12" }, "org.glassfish.jaxb:jaxb-runtime": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -1504,35 +1510,35 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "redis.clients:jedis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence" ], - "locked": "3.8.0" + "locked": "3.3.0" } } } \ No newline at end of file diff --git a/springboot-bom-overrides.gradle b/springboot-bom-overrides.gradle index dc9f79b60..328a12a2a 100644 --- a/springboot-bom-overrides.gradle +++ b/springboot-bom-overrides.gradle @@ -12,7 +12,7 @@ */ // Contains overrides for Spring Boot Dependency Management plugin -// Dependency version override properties can be found at https://docs.spring.io/spring-boot/docs/2.7.3/reference/htmlsingle/#appendix.dependency-versions.properties +// Dependency version override properties can be found at https://docs.spring.io/spring-boot/docs/3.1.4/reference/htmlsingle/#appendix.dependency-versions.properties // Conductor's default is ES6, but SB brings in ES7 ext['elasticsearch.version'] = revElasticSearch6 diff --git a/test-harness/build.gradle b/test-harness/build.gradle index 7adc179e3..8ef63eb8d 100644 --- a/test-harness/build.gradle +++ b/test-harness/build.gradle @@ -28,7 +28,7 @@ dependencies { testImplementation "redis.clients:jedis:${revJedis}" testImplementation "com.netflix.dyno-queues:dyno-queues-redis:${revDynoQueues}" - testImplementation "org.codehaus.groovy:groovy-all:${revGroovy}" + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 2dd48417d..95bb8411d 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -1,55 +1,55 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.3" + "locked": "3.1.4" } }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" } }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.3" + "locked": "2.13.5" }, "com.google.guava:guava": { "locked": "30.0-jre" }, "com.google.protobuf:protobuf-java": { - "locked": "3.21.7" + "locked": "3.21.12" }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true @@ -100,22 +100,22 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.elasticsearch.client:elasticsearch-rest-client": { "locked": "6.8.12" @@ -130,22 +130,22 @@ "locked": "5.8.2" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "locked": "1.3.3" }, "org.springframework:spring-web": { - "locked": "5.3.22" + "locked": "6.0.12" }, "org.testcontainers:elasticsearch": { "locked": "1.15.3" @@ -183,39 +183,39 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.3" + "locked": "2.13.5" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -239,7 +239,7 @@ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client" ], - "locked": "3.21.7" + "locked": "3.21.12" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -474,11 +474,11 @@ ], "locked": "2.3.3" }, - "javax.annotation:javax.annotation-api": { + "jakarta.annotation:jakarta.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.3.2" + "locked": "2.1.1" }, "javax.ws.rs:javax.ws.rs-api": { "firstLevelTransitive": [ @@ -545,7 +545,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -568,7 +568,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -591,7 +591,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -614,7 +614,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.17.2" + "locked": "2.21.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -637,10 +637,10 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.17.2" + "locked": "2.21.0" }, - "org.codehaus.groovy:groovy-all": { - "locked": "2.5.22" + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ @@ -701,10 +701,10 @@ "locked": "1.7.36" }, "org.spockframework:spock-core": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.spockframework:spock-spring": { - "locked": "1.3-groovy-2.5" + "locked": "2.4-M1-groovy-4.0" }, "org.springdoc:springdoc-openapi-ui": { "firstLevelTransitive": [ @@ -717,35 +717,35 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-actuator": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.7.3" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { "firstLevelTransitive": [ @@ -754,7 +754,7 @@ "locked": "1.3.3" }, "org.springframework:spring-web": { - "locked": "5.3.22" + "locked": "6.0.12" }, "org.testcontainers:elasticsearch": { "locked": "1.15.3" diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy index 8a70dabf0..4f13a99df 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy @@ -12,8 +12,6 @@ */ package com.netflix.conductor.test.util -import javax.annotation.PostConstruct - import org.apache.commons.lang3.StringUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @@ -31,6 +29,7 @@ import com.netflix.conductor.service.ExecutionService import com.netflix.conductor.service.MetadataService import com.fasterxml.jackson.databind.ObjectMapper +import jakarta.annotation.PostConstruct /** * This is a helper class used to initialize task definitions required by the tests when loaded up. diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java index 09f7b25b3..e04a2a991 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; From 4762cc2f0485ca956cf9d78cb8173c9d795197f5 Mon Sep 17 00:00:00 2001 From: yanghaojia <2453883990@qq.com> Date: Mon, 27 Nov 2023 16:26:41 +0800 Subject: [PATCH 002/202] Fix typo in WorkflowTaskTypeConstraint --- .../conductor/validations/WorkflowTaskTypeConstraint.java | 4 ++-- .../conductor/validations/WorkflowTaskTypeConstraintTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java index 99d070985..493fd76b2 100644 --- a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java +++ b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java @@ -262,7 +262,7 @@ private boolean isDoWhileTaskValid( String message = String.format( PARAM_REQUIRED_STRING_FORMAT, - "loopExpression", + "loopCondition", TaskType.DO_WHILE, workflowTask.getName()); context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); @@ -272,7 +272,7 @@ private boolean isDoWhileTaskValid( String message = String.format( PARAM_REQUIRED_STRING_FORMAT, - "loopover", + "loopOver", TaskType.DO_WHILE, workflowTask.getName()); context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java index a1105ed72..a15393248 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java @@ -170,10 +170,10 @@ public void testWorkflowTaskTypeDoWhile() { assertTrue( validationErrors.contains( - "loopExpression field is required for taskType: DO_WHILE taskName: encode")); + "loopCondition field is required for taskType: DO_WHILE taskName: encode")); assertTrue( validationErrors.contains( - "loopover field is required for taskType: DO_WHILE taskName: encode")); + "loopOver field is required for taskType: DO_WHILE taskName: encode")); } @Test From 17c6db1da0f902ec0d0cc7c6141850c3de56781a Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Thu, 7 Dec 2023 23:03:26 -0800 Subject: [PATCH 003/202] update readme with the links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index adb7c1c1b..6b5a74092 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [![GitHub forks](https://img.shields.io/github/forks/Netflix/conductor.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/Netflix/conductor/network/) -Conductor is a platform created by Netflix to orchestrate workflows that span across microservices. -Conductor is maintained by Media Workflow Infrastructure team at Netflix. +Conductor is a platform originally created at Netflix to orchestrate microservices and events. +This Conductor repository is maintained by the team at orkes.io and a group of volunteers from the community. -For more information, see [Main Documentation Site](https://conductor.netflix.com/) +For more information, see [Main Documentation Site](https://orkes.io/content) ## Releases From b412fe3c01e1ab47fa3add73a3bec49ef36bb4d2 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Thu, 7 Dec 2023 23:07:21 -0800 Subject: [PATCH 004/202] Update README.md --- README.md | 61 ++++++++----------------------------------------------- 1 file changed, 8 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 6b5a74092..d0221bd35 100644 --- a/README.md +++ b/README.md @@ -11,32 +11,28 @@ Conductor is a platform originally created at Netflix to orchestrate microservices and events. -This Conductor repository is maintained by the team at orkes.io and a group of volunteers from the community. +This Conductor repository is maintained by the team at [Orkes](https://orkes.io/content) and a group of volunteers from the community. For more information, see [Main Documentation Site](https://orkes.io/content) ## Releases The latest version is [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) - -[2.31.8](https://github.com/Netflix/conductor/releases/tag/v2.31.8) is the **final** release of `2.31` branch. As of Feb 2022, `1.x` & `2.x` versions are no longer supported. +The next scheduled release is in Dec 2023. ## Resources #### [Slack Community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) We have an active [community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) of Conductor users and contributors on the channel. -#### [Documentation Site](https://conductor.netflix.com/) -[Documentation](https://conductor.netflix.com/) and tutorial on how to use Conductor +#### [Documentation Site](https://orkes.io/content) +[Documentation](https://orkes.io/content) and tutorial on how to use Conductor -## Workflow Creation in Code +## Workflow SDKs Conductor supports creating workflows using JSON and Code. SDK support for creating workflows using code is available in multiple languages and can be found at https://github.com/conductor-sdk -## Community Contributions -The modules contributed by the community are housed at [conductor-community](https://github.com/Netflix/conductor-community). Compatible versions of the community modules are released simultaneously with releases of the main modules. -[Discussion Forum](https://github.com/Netflix/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. +[Discussion Forum](https://github.com/conductor-oss/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. -[List of Conductor community projects](/docs/docs/resources/related.md): Backup tool, Cron like workflow starter, Docker containers and more. ## Getting Started - Building & Running Conductor ### Using Docker: @@ -45,36 +41,11 @@ The easiest way to get started is with Docker containers. Please follow the inst ### From Source: Conductor Server is a [Spring Boot](https://spring.io/projects/spring-boot) project and follows all applicable conventions. See instructions [here](https://conductor.netflix.com/devguide/running/source.html). -## Published Artifacts -Binaries are available from the [Maven Central Repository](https://search.maven.org/search?q=g:com.netflix.conductor). - -| Artifact | Description | -|---------------------------------|-------------------------------------------------------------------------------------------------| -| conductor-common | Common models used by various conductor modules | -| conductor-core | Core Conductor module | -| conductor-redis-persistence | Persistence and queue using Redis/Dynomite | -| conductor-cassandra-persistence | Persistence using Cassandra | -| conductor-es6-persistence | Indexing using Elasticsearch 6.X | -| conductor-rest | Spring MVC resources for the core services | -| conductor-ui | node.js based UI for Conductor | -| conductor-client | Java client for Conductor that includes helpers for running worker tasks | -| conductor-client-spring | Client starter kit for Spring | -| conductor-java-sdk | SDK for writing workflows in code | -| conductor-server | Spring Boot Web Application | -| conductor-redis-lock | Workflow execution lock implementation using Redis | -| conductor-awss3-storage | External payload storage implementation using AWS S3 | -| conductor-awssqs-event-queue | Event queue implementation using AWS SQS | -| conductor-http-task | Workflow system task implementation to send make requests | -| conductor-json-jq-task | Workflow system task implementation to evaluate JSON using [jq](https://stedolan.github.io/jq/) | -| conductor-grpc | Protobuf models used by the server and client | -| conductor-grpc-client | gRPC client to interact with the gRPC server | -| conductor-grpc-server | gRPC server Application | -| conductor-test-harness | Integration and regression tests | - ## Database Requirements * The default persistence used is Redis * The indexing backend is [Elasticsearch](https://www.elastic.co/) (6.x) +* The default queues are [orkes-queues](https://github.com/orkes-io/orkes-queues) ## Other Requirements * JDK 17+ @@ -83,24 +54,8 @@ Binaries are available from the [Maven Central Repository](https://search.maven. ## Get Support There are several ways to get in touch with us: * [Slack Community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) -* [GitHub Discussion Forum](https://github.com/Netflix/conductor/discussions) ## Contributions Whether it is a small documentation correction, bug fix or a new feature, contributions are highly appreciated. We just ask you to follow standard OSS guidelines. The [Discussion Forum](https://github.com/Netflix/conductor/discussions) is a good place to ask questions, discuss new features and explore ideas. Please check with us before spending too much time, only to find out later that someone else is already working on a similar feature. -`main` branch is the current working branch. Please send your PR's to `main` branch, making sure that it builds on your local system successfully. Also, please make sure all the conflicts are resolved. - -## License -Copyright 2022 Netflix, Inc. - -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. +`main` branch is the current working branch. Please send your PR's to `main` branch, making sure that it builds on your local system successfully. Also, please make sure all the conflicts are resolved. \ No newline at end of file From 18cb5b28f639389d13ad240f98a896faf23b41d4 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Thu, 7 Dec 2023 23:26:10 -0800 Subject: [PATCH 005/202] docker and docs --- README.md | 37 +++++++++++++++++++++++++++++++++---- docker/server/Dockerfile | 8 ++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d0221bd35..4113f34bd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) [![CI](https://github.com/Netflix/conductor/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Netflix/conductor/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Netflix/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) -[![NetflixOSS Lifecycle](https://img.shields.io/osslifecycle/Netflix/conductor.svg)]() [![GitHub stars](https://img.shields.io/github/stars/Netflix/conductor.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/Netflix/conductor/stargazers/) [![GitHub forks](https://img.shields.io/github/forks/Netflix/conductor.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/Netflix/conductor/network/) @@ -18,6 +17,7 @@ For more information, see [Main Documentation Site](https://orkes.io/content) ## Releases The latest version is [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) + The next scheduled release is in Dec 2023. ## Resources @@ -35,11 +35,40 @@ SDK support for creating workflows using code is available in multiple languages ## Getting Started - Building & Running Conductor -### Using Docker: -The easiest way to get started is with Docker containers. Please follow the instructions [here](https://conductor.netflix.com/devguide/running/docker.html). ### From Source: -Conductor Server is a [Spring Boot](https://spring.io/projects/spring-boot) project and follows all applicable conventions. See instructions [here](https://conductor.netflix.com/devguide/running/source.html). +If you wish to build your own distribution, you can run ```./gradlew build``` from this project that products the runtime artifacts. +The runnable server is in server/ module. + +### Using Docker (Recommended) +A production ready container is published by Orkes from (https://github.com/orkes-io/orkes-conductor-community) + +Follow the steps below to launch the docker container: + +#### Simple self-contained script to launch the docker image +```shell +curl https://raw.githubusercontent.com/orkes-io/orkes-conductor-community/main/scripts/run_local.sh | sh +``` + +#### Using docker run manually (Provides more control) +```shell + +# Create volumes for persistent stores +# Used to create a persistent volume that will preserve the +docker volume create postgres +docker volume create redis + +docker run --init -p 8080:8080 -p 1234:5000 --mount source=redis,target=/redis \ +--mount source=postgres,target=/pgdata orkesio/orkes-conductor-community-standalone:latest +``` + +Navigate to http://localhost:1234 once the container starts to launch UI. + +## Docker Containers for production usage +```shell +docker pull orkesio/orkes-conductor-community:latest +``` + ## Database Requirements diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 793cb830a..9b44f47a9 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,12 +1,12 @@ # -# conductor:server - Combined Netflix conductor server & UI +# conductor:server - Combined conductor server & UI # # =========================================================================================================== # 0. Builder stage # =========================================================================================================== FROM alpine:3.18 AS builder -LABEL maintainer="Netflix OSS " +LABEL maintainer="Orkes OSS " # =========================================================================================================== # 0. Build Conductor Server @@ -32,7 +32,7 @@ RUN mkdir server-build WORKDIR server-build RUN ls -ltr -RUN git clone https://github.com/Netflix/conductor-community.git +RUN git clone https://github.com/conductor-oss/conductor-community.git # Copy the project directly onto the image WORKDIR conductor-community @@ -50,7 +50,7 @@ RUN pwd # =========================================================================================================== FROM alpine:3.18 -LABEL maintainer="Netflix OSS " +LABEL maintainer="Orkes OSS " RUN apk add openjdk17 RUN apk add nginx From 3a67c25075ebf4baa620343f7a31e40fc2897578 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Thu, 7 Dec 2023 23:38:04 -0800 Subject: [PATCH 006/202] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4113f34bd..aff6875e0 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,13 @@ We have an active [community](https://join.slack.com/t/orkes-conductor/shared_in #### [Documentation Site](https://orkes.io/content) [Documentation](https://orkes.io/content) and tutorial on how to use Conductor -## Workflow SDKs +[Discussion Forum](https://github.com/conductor-oss/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. + +### Conductor SDKs Conductor supports creating workflows using JSON and Code. SDK support for creating workflows using code is available in multiple languages and can be found at https://github.com/conductor-sdk -[Discussion Forum](https://github.com/conductor-oss/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. - - ## Getting Started - Building & Running Conductor ### From Source: From d7373de5e955a4c83a4cd3704e57eb7dccb2d9d0 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Fri, 8 Dec 2023 13:29:06 -0800 Subject: [PATCH 007/202] update code of conduct --- CODE_OF_CONDUCT.md | 2 +- docs/docs/resources/code-of-conduct.md | 49 -------------------------- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 docs/docs/resources/code-of-conduct.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b9c30c329..425e79778 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1 +1 @@ -[Code of Conduct](docs/docs/resources/code-of-conduct.md) \ No newline at end of file +[Code of Conduct](https://orkes.io/orkes-conductor-community-code-of-conduct) \ No newline at end of file diff --git a/docs/docs/resources/code-of-conduct.md b/docs/docs/resources/code-of-conduct.md deleted file mode 100644 index f8076bc62..000000000 --- a/docs/docs/resources/code-of-conduct.md +++ /dev/null @@ -1,49 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at netflixoss@netflix.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq From cf0cfe47567389b510110004bbead7d5541ca513 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Fri, 8 Dec 2023 13:31:21 -0800 Subject: [PATCH 008/202] update readme --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index aff6875e0..cab6c236f 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,3 @@ docker pull orkesio/orkes-conductor-community:latest ## Get Support There are several ways to get in touch with us: * [Slack Community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) - -## Contributions -Whether it is a small documentation correction, bug fix or a new feature, contributions are highly appreciated. We just ask you to follow standard OSS guidelines. The [Discussion Forum](https://github.com/Netflix/conductor/discussions) is a good place to ask questions, discuss new features and explore ideas. Please check with us before spending too much time, only to find out later that someone else is already working on a similar feature. - -`main` branch is the current working branch. Please send your PR's to `main` branch, making sure that it builds on your local system successfully. Also, please make sure all the conflicts are resolved. \ No newline at end of file From 8af6706c790e1aaf8ce075cc8acbb1083da90b8d Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Fri, 8 Dec 2023 13:58:13 -0800 Subject: [PATCH 009/202] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index cab6c236f..f8d109e99 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ This Conductor repository is maintained by the team at [Orkes](https://orkes.io/ For more information, see [Main Documentation Site](https://orkes.io/content) +## Update your forks +Please update your forks to point to this repo. This will ensure your commits and PRs can be send against this repository +```shell +git remote set-url origin https://github.com/conductor-oss/conductor +``` ## Releases The latest version is [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) From 06e61158bfbf3a1c8dbc6b912ddff4ed3e30fda7 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 12 Dec 2023 13:19:39 -0800 Subject: [PATCH 010/202] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f8d109e99..53a491358 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ This Conductor repository is maintained by the team at [Orkes](https://orkes.io/ For more information, see [Main Documentation Site](https://orkes.io/content) +## Star this repo +Show support for the Conductor OSS. Please help spread the awareness by starring this repo. + ## Update your forks Please update your forks to point to this repo. This will ensure your commits and PRs can be send against this repository ```shell From d888738c19d011a78364bb9b03ea5cbdacb13c9b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 12 Dec 2023 13:21:41 -0800 Subject: [PATCH 011/202] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 53a491358..82e995750 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,11 @@ Please update your forks to point to this repo. This will ensure your commits a ```shell git remote set-url origin https://github.com/conductor-oss/conductor ``` +> [!IMPORTANT] +> **Follow the steps below if you have an active PR againt the Netflix/Conductor repository** +> 1. Fork this repository +> 2. Update your local repository to change the remote to this repository +> 3. Send a PR against the `main` branch ## Releases The latest version is [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) From 673d7173089d483d86645aad3739539acbf3af84 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 12 Dec 2023 13:29:41 -0800 Subject: [PATCH 012/202] Update README.md --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 82e995750..8abfeb03d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ [![CI](https://github.com/Netflix/conductor/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Netflix/conductor/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Netflix/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) -[![GitHub stars](https://img.shields.io/github/stars/Netflix/conductor.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/Netflix/conductor/stargazers/) -[![GitHub forks](https://img.shields.io/github/forks/Netflix/conductor.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/Netflix/conductor/network/) + + Conductor is a platform originally created at Netflix to orchestrate microservices and events. @@ -14,17 +14,19 @@ This Conductor repository is maintained by the team at [Orkes](https://orkes.io/ For more information, see [Main Documentation Site](https://orkes.io/content) -## Star this repo +## Star this repo Show support for the Conductor OSS. Please help spread the awareness by starring this repo. -## Update your forks +[![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/conductor-oss/conductor/stargazers/) + +## Update your local forks/clones Please update your forks to point to this repo. This will ensure your commits and PRs can be send against this repository ```shell git remote set-url origin https://github.com/conductor-oss/conductor ``` > [!IMPORTANT] > **Follow the steps below if you have an active PR againt the Netflix/Conductor repository** -> 1. Fork this repository +> 1. Fork **this** repository > 2. Update your local repository to change the remote to this repository > 3. Send a PR against the `main` branch From 83d6d8c5e15171a0e48f5d50bdb3871a65664ca7 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 12 Dec 2023 13:43:21 -0800 Subject: [PATCH 013/202] Update README.md --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8abfeb03d..c3dd4cf40 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,26 @@ ![Conductor](docs/docs/img/logo.png) -# Conductor [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) [![CI](https://github.com/Netflix/conductor/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Netflix/conductor/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Netflix/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) - - - Conductor is a platform originally created at Netflix to orchestrate microservices and events. This Conductor repository is maintained by the team at [Orkes](https://orkes.io/content) and a group of volunteers from the community. For more information, see [Main Documentation Site](https://orkes.io/content) -## Star this repo +## Conductor OSS +This is the new home for the Conductor open source going forward (previously hosted at Netflix/Conductor). +> [!IMPORTANT] +> Going forward, all the bug fixes, feature requests and security patches will be applied and released from this repository. + + +The last published version of Netflix Condutor will be **3.15.0** which we will continue to support. + +If you would like to participate in the roadmap and development, [please reach out](https://forms.gle/P2i1xHrxPQLrjzTB7). + +## ⭐ This repository Show support for the Conductor OSS. Please help spread the awareness by starring this repo. [![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/conductor-oss/conductor/stargazers/) From 9b2b5a6681d86d969a177a8b5ffc86ef8ee9e549 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 08:55:23 -0800 Subject: [PATCH 014/202] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3dd4cf40..eacbcda48 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,14 @@ docker volume create postgres docker volume create redis docker run --init -p 8080:8080 -p 1234:5000 --mount source=redis,target=/redis \ ---mount source=postgres,target=/pgdata orkesio/orkes-conductor-community-standalone:latest +--mount source=postgres,target=/pgdata conductoross/conductor-standalone:3.15.0 ``` Navigate to http://localhost:1234 once the container starts to launch UI. ## Docker Containers for production usage ```shell -docker pull orkesio/orkes-conductor-community:latest +docker pull conductoross/conductor:3.15.0 ``` From adbfac6119cf3178bffe3e36b768fe38e42c04c0 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 10:05:29 -0800 Subject: [PATCH 015/202] Update README.md --- README.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eacbcda48..1a791726f 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ Conductor is a platform originally created at Netflix to orchestrate microservices and events. -This Conductor repository is maintained by the team at [Orkes](https://orkes.io/content) and a group of volunteers from the community. -For more information, see [Main Documentation Site](https://orkes.io/content) ## Conductor OSS This is the new home for the Conductor open source going forward (previously hosted at Netflix/Conductor). @@ -44,8 +42,8 @@ The next scheduled release is in Dec 2023. ## Resources #### [Slack Community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) We have an active [community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) of Conductor users and contributors on the channel. -#### [Documentation Site](https://orkes.io/content) -[Documentation](https://orkes.io/content) and tutorial on how to use Conductor +#### [Documentation Site](https://conductor.netflix.com) +[Documentation](https://conductor.netflix.com) and tutorial on how to use Conductor [Discussion Forum](https://github.com/conductor-oss/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. @@ -61,15 +59,8 @@ If you wish to build your own distribution, you can run ```./gradlew build``` fr The runnable server is in server/ module. ### Using Docker (Recommended) -A production ready container is published by Orkes from (https://github.com/orkes-io/orkes-conductor-community) - Follow the steps below to launch the docker container: -#### Simple self-contained script to launch the docker image -```shell -curl https://raw.githubusercontent.com/orkes-io/orkes-conductor-community/main/scripts/run_local.sh | sh -``` - #### Using docker run manually (Provides more control) ```shell @@ -94,7 +85,6 @@ docker pull conductoross/conductor:3.15.0 * The default persistence used is Redis * The indexing backend is [Elasticsearch](https://www.elastic.co/) (6.x) -* The default queues are [orkes-queues](https://github.com/orkes-io/orkes-queues) ## Other Requirements * JDK 17+ From aa31ca1872e01fb3cee5515b1af349db44a1b5b3 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 10:08:26 -0800 Subject: [PATCH 016/202] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 1a791726f..f1df1d9a6 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,6 @@ The runnable server is in server/ module. ### Using Docker (Recommended) Follow the steps below to launch the docker container: -#### Using docker run manually (Provides more control) ```shell # Create volumes for persistent stores From 11ae434b3c7a18857f64b2af17e46ef65efa1e43 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 10:32:08 -0800 Subject: [PATCH 017/202] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f1df1d9a6..333dffe57 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Conductor is a platform originally created at Netflix to orchestrate microservices and events. - +Condutor OSS is maintained by the team of developers at Orkes along with the members of the open source community. ## Conductor OSS This is the new home for the Conductor open source going forward (previously hosted at Netflix/Conductor). @@ -42,8 +42,8 @@ The next scheduled release is in Dec 2023. ## Resources #### [Slack Community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) We have an active [community](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg) of Conductor users and contributors on the channel. -#### [Documentation Site](https://conductor.netflix.com) -[Documentation](https://conductor.netflix.com) and tutorial on how to use Conductor +#### [Documentation Site](https://orkes.io/content) +[Documentation](https://orkes.io/content) and tutorial on how to use Conductor [Discussion Forum](https://github.com/conductor-oss/conductor/discussions): Please use the forum for questions and discussing ideas and join the community. From f1136f7bb16db6c2088fc48cb83347d2a534acca Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 10:33:22 -0800 Subject: [PATCH 018/202] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 333dffe57..06c5ccfaa 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Conductor is a platform originally created at Netflix to orchestrate microservices and events. -Condutor OSS is maintained by the team of developers at Orkes along with the members of the open source community. +Condutor OSS is maintained by the team of developers at [Orkes](https://orkes.io/) along with the members of the open source community. ## Conductor OSS This is the new home for the Conductor open source going forward (previously hosted at Netflix/Conductor). From 1c3442eb059f83f029a15ced05d62df38f08a71f Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 10:54:01 -0800 Subject: [PATCH 019/202] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06c5ccfaa..693422cf7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ If you would like to participate in the roadmap and development, [please reach o ## ⭐ This repository Show support for the Conductor OSS. Please help spread the awareness by starring this repo. -[![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/conductor-oss/conductor/stargazers/) +[![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=)](https://GitHub.com/conductor-oss/conductor/) ## Update your local forks/clones Please update your forks to point to this repo. This will ensure your commits and PRs can be send against this repository From e8dec7ad63a9052e12e591a6fd0628265c68dd5e Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 11:34:47 -0800 Subject: [PATCH 020/202] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 693422cf7..5c291a8af 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Conductor is a platform originally created at Netflix to orchestrate microservices and events. -Condutor OSS is maintained by the team of developers at [Orkes](https://orkes.io/) along with the members of the open source community. +Conductor OSS is maintained by the team of developers at [Orkes](https://orkes.io/) along with the members of the open source community. ## Conductor OSS This is the new home for the Conductor open source going forward (previously hosted at Netflix/Conductor). From c68ca4e58f0ae3a5c2740a1e71c1da0c2319ab81 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 11:36:58 -0800 Subject: [PATCH 021/202] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c291a8af..d29467162 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ docker pull conductoross/conductor:3.15.0 ## Other Requirements * JDK 17+ -* UI requires Node 14 to build. Earlier Node versions may work but is untested. +* UI requires Node 14 to build. Earlier Node versions may work but are untested. ## Get Support There are several ways to get in touch with us: From 3c15237fdf8a1f93c2885834af76cc25a5db8b8b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 13 Dec 2023 12:36:50 -0800 Subject: [PATCH 022/202] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d29467162..c9525ae62 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ ![Conductor](docs/docs/img/logo.png) [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) -[![CI](https://github.com/Netflix/conductor/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Netflix/conductor/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Netflix/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) From 92f06bf1bf397d5d92ff9a835d59a3e2e5bb51bf Mon Sep 17 00:00:00 2001 From: Iva Koleva Date: Thu, 14 Dec 2023 10:25:00 +0200 Subject: [PATCH 023/202] Permissive task capability Why: We need idempotent forked tasks, meaning all tasks get executed, but any failures are still detected upon join. Feature request https://github.com/Netflix/conductor/issues/3861 What: Introduced the concept of Permissive tasks. A Permissive task is similar to a Simple task. The difference is, it permits the other tasks to continue - in case a Permissive task failed. Result is: 1. Forked Permissive tasks will let each other be evaluated, until all the forked tasks had terminated. Only then, the join task should fail. In case of Permissive optional tasks, the join will not fail. 2. Permissive sequential tasks will let subsequent tasks continue. While at the end, the workflow will fail in case a permissive task had failed. The workflow would not fail in case of Permissive optional task failure. Testing done: PermissiveTaskMapperTest added, TestDeciderOutcomes.testPermissive() added, WorkflowAndTaskConfigurationSpec "Test simple workflow which has a permissive task" and "Test simple workflow which has a permissive optional task added" that cover retry, ForkJoinSpec "Test a simple workflow with fork join permissive failure flow" added and "Test retrying a failed permissive fork join workflow" added. In addition, performed e2e tests locally running a Conductor instance. Did build a docker image with the code changes made, started it locally, and started a SampleWorker to poll 3 tasks in parallel. Verified e2e scenarios of task_def_permissive, task_def_permissive_optional, task_def_simple.json, task_def_simple_optional.json, each joining on 6 forked tasks, then running simple task 7 after join. --- .../common/metadata/tasks/TaskType.java | 2 + .../core/execution/DeciderService.java | 49 +++- .../core/execution/WorkflowExecutor.java | 22 ++ .../mapper/PermissiveTaskMapper.java | 102 +++++++ .../core/execution/tasks/ExclusiveJoin.java | 16 +- .../conductor/core/execution/tasks/Join.java | 17 +- .../core/execution/TestDeciderOutcomes.java | 74 +++++ .../mapper/PermissiveTaskMapperTest.java | 114 ++++++++ .../workflow/def/tasks/PermissiveTask.java | 47 +++ .../workflow/executor/WorkflowExecutor.java | 1 + .../test/integration/ForkJoinSpec.groovy | 273 +++++++++++++++++- .../WorkflowAndTaskConfigurationSpec.groovy | 159 +++++++++- ...fork_join_permissive_integration_test.json | 109 +++++++ ...ermissive_task_retry_integration_test.json | 190 ++++++++++++ ...issive_optional_task_integration_test.json | 58 ++++ ...with_permissive_task_integration_test.json | 92 ++++++ 16 files changed, 1311 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java create mode 100644 core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java create mode 100644 java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java create mode 100644 test-harness/src/test/resources/fork_join_permissive_integration_test.json create mode 100644 test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json create mode 100644 test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json create mode 100644 test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java index 235a0ac91..efc985acd 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -23,6 +23,7 @@ public enum TaskType { DYNAMIC, FORK_JOIN, FORK_JOIN_DYNAMIC, + PERMISSIVE, DECISION, SWITCH, JOIN, @@ -70,6 +71,7 @@ public enum TaskType { public static final String TASK_TYPE_JSON_JQ_TRANSFORM = "JSON_JQ_TRANSFORM"; public static final String TASK_TYPE_SET_VARIABLE = "SET_VARIABLE"; public static final String TASK_TYPE_FORK = "FORK"; + public static final String TASK_TYPE_PERMISSIVE = "PERMISSIVE"; public static final String TASK_TYPE_NOOP = "NOOP"; private static final Set BUILT_IN_TASKS = new HashSet<>(); diff --git a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java index c6bf486ba..52afc52c2 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java @@ -44,6 +44,7 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; +import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TERMINATE; import static com.netflix.conductor.common.metadata.tasks.TaskType.USER_DEFINED; import static com.netflix.conductor.model.TaskModel.Status.*; @@ -207,7 +208,11 @@ private DeciderOutcome decide(final WorkflowModel workflow, List preS tasksToBeScheduled.put(retryTask.get().getReferenceTaskName(), retryTask.get()); executedTaskRefNames.remove(retryTask.get().getReferenceTaskName()); outcome.tasksToBeUpdated.add(pendingTask); - } else { + } else if (!(pendingTask.getWorkflowTask() != null + && TaskType.PERMISSIVE + .name() + .equals(pendingTask.getWorkflowTask().getType()) + && !pendingTask.getWorkflowTask().isOptional())) { pendingTask.setStatus(COMPLETED_WITH_ERRORS); } } @@ -254,6 +259,39 @@ private DeciderOutcome decide(final WorkflowModel workflow, List preS if (hasSuccessfulTerminateTask || (outcome.tasksToBeScheduled.isEmpty() && checkForWorkflowCompletion(workflow))) { LOGGER.debug("Marking workflow: {} as complete.", workflow); + List permissiveTasksTerminalNonSuccessful = + workflow.getTasks().stream() + .filter(t -> t.getWorkflowTask() != null) + .filter(t -> PERMISSIVE.name().equals(t.getWorkflowTask().getType())) + .filter(t -> !t.getWorkflowTask().isOptional()) + .collect( + Collectors.toMap( + TaskModel::getReferenceTaskName, + t -> t, + (t1, t2) -> + t1.getRetryCount() > t2.getRetryCount() + ? t1 + : t2)) + .values() + .stream() + .filter( + t -> + t.getStatus().isTerminal() + && !t.getStatus().isSuccessful()) + .toList(); + if (!permissiveTasksTerminalNonSuccessful.isEmpty()) { + final String errMsg = + permissiveTasksTerminalNonSuccessful.stream() + .map( + t -> + String.format( + "Task %s failed with status: %s and reason: '%s'", + t.getTaskId(), + t.getStatus(), + t.getReasonForIncompletion())) + .collect(Collectors.joining(". ")); + throw new TerminateWorkflowException(errMsg); + } outcome.isComplete = true; } @@ -437,11 +475,6 @@ public boolean checkForWorkflowCompletion(final WorkflowModel workflow) if (status == null || !status.isTerminal()) { return false; } - // if we reach here, the task has been completed. - // Was the task successful in completion? - if (!status.isSuccessful()) { - return false; - } } boolean noPendingSchedule = @@ -529,7 +562,9 @@ Optional retry( if (!task.getStatus().isRetriable() || TaskType.isBuiltIn(task.getTaskType()) || expectedRetryCount <= retryCount) { - if (workflowTask != null && workflowTask.isOptional()) { + if (workflowTask != null + && (workflowTask.isOptional() + || TaskType.PERMISSIVE.name().equals(workflowTask.getType()))) { return Optional.empty(); } WorkflowModel.Status status; diff --git a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java index 6070741a2..37bc358bb 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java @@ -336,6 +336,18 @@ private void retry(WorkflowModel workflow) { for (TaskModel task : workflow.getTasks()) { switch (task.getStatus()) { case FAILED: + if (task.getTaskType().equalsIgnoreCase(TaskType.JOIN.toString()) + || task.getTaskType() + .equalsIgnoreCase(TaskType.EXCLUSIVE_JOIN.toString())) { + @SuppressWarnings("unchecked") + List joinOn = (List) task.getInputData().get("joinOn"); + boolean joinOnFailedPermissive = isJoinOnFailedPermissive(joinOn, workflow); + if (joinOnFailedPermissive) { + task.setStatus(IN_PROGRESS); + addTaskToQueue(task); + break; + } + } case FAILED_WITH_TERMINAL_ERROR: case TIMED_OUT: retriableMap.put(task.getReferenceTaskName(), task); @@ -1814,4 +1826,14 @@ private void expediteLazyWorkflowEvaluation(String workflowId) { LOGGER.info("Pushed workflow {} to {} for expedited evaluation", workflowId, DECIDER_QUEUE); } + + private static boolean isJoinOnFailedPermissive(List joinOn, WorkflowModel workflow) { + return joinOn.stream() + .map(workflow::getTaskByRefName) + .anyMatch( + t -> + TaskType.PERMISSIVE.name().equals(t.getWorkflowTask().getType()) + && !t.getWorkflowTask().isOptional() + && t.getStatus().equals(FAILED)); + } } diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java new file mode 100644 index 000000000..124a9b2e0 --- /dev/null +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.core.execution.mapper; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.core.exception.TerminateWorkflowException; +import com.netflix.conductor.core.utils.ParametersUtils; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +/** + * An implementation of {@link TaskMapper} to map a {@link WorkflowTask} of type {@link + * TaskType#PERMISSIVE} to a {@link TaskModel} with status {@link TaskModel.Status#SCHEDULED}. + */ +@Component +public class PermissiveTaskMapper implements TaskMapper { + + public static final Logger LOGGER = LoggerFactory.getLogger(PermissiveTaskMapper.class); + private final ParametersUtils parametersUtils; + + public PermissiveTaskMapper(ParametersUtils parametersUtils) { + this.parametersUtils = parametersUtils; + } + + @Override + public String getTaskType() { + return TaskType.PERMISSIVE.name(); + } + + /** + * This method maps a {@link WorkflowTask} of type {@link TaskType#PERMISSIVE} to a {@link + * TaskModel} + * + * @param taskMapperContext: A wrapper class containing the {@link WorkflowTask}, {@link + * WorkflowDef}, {@link WorkflowModel} and a string representation of the TaskId + * @return a List with just one exclusive task + * @throws TerminateWorkflowException In case if the task definition does not exist + */ + @Override + public List getMappedTasks(TaskMapperContext taskMapperContext) + throws TerminateWorkflowException { + + LOGGER.debug("TaskMapperContext {} in PermissiveTaskMapper", taskMapperContext); + + WorkflowTask workflowTask = taskMapperContext.getWorkflowTask(); + WorkflowModel workflowModel = taskMapperContext.getWorkflowModel(); + int retryCount = taskMapperContext.getRetryCount(); + String retriedTaskId = taskMapperContext.getRetryTaskId(); + + TaskDef taskDefinition = + Optional.ofNullable(workflowTask.getTaskDefinition()) + .orElseThrow( + () -> { + String reason = + String.format( + "Invalid task. Task %s does not have a definition", + workflowTask.getName()); + return new TerminateWorkflowException(reason); + }); + + Map input = + parametersUtils.getTaskInput( + workflowTask.getInputParameters(), + workflowModel, + taskDefinition, + taskMapperContext.getTaskId()); + TaskModel permissiveTask = taskMapperContext.createTaskModel(); + permissiveTask.setTaskType(workflowTask.getName()); + permissiveTask.setStartDelayInSeconds(workflowTask.getStartDelay()); + permissiveTask.setInputData(input); + permissiveTask.setStatus(TaskModel.Status.SCHEDULED); + permissiveTask.setRetryCount(retryCount); + permissiveTask.setCallbackAfterSeconds(workflowTask.getStartDelay()); + permissiveTask.setResponseTimeoutSeconds(taskDefinition.getResponseTimeoutSeconds()); + permissiveTask.setRetriedTaskId(retriedTaskId); + permissiveTask.setRateLimitPerFrequency(taskDefinition.getRateLimitPerFrequency()); + permissiveTask.setRateLimitFrequencyInSeconds( + taskDefinition.getRateLimitFrequencyInSeconds()); + return List.of(permissiveTask); + } +} diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java index e2bf0ac0b..62a02c3c9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java @@ -24,6 +24,7 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; +import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_EXCLUSIVE_JOIN; @Component(TASK_TYPE_EXCLUSIVE_JOIN) @@ -65,9 +66,20 @@ public boolean execute( } taskStatus = exclusiveTask.getStatus(); foundExlusiveJoinOnTask = taskStatus.isTerminal(); - hasFailures = !taskStatus.isSuccessful(); + hasFailures = + !taskStatus.isSuccessful() + && (!PERMISSIVE.name().equals(exclusiveTask.getWorkflowTask().getType()) + || joinOn.stream() + .map(workflow::getTaskByRefName) + .allMatch(t -> t.getStatus().isTerminal())); if (hasFailures) { - failureReason.append(exclusiveTask.getReasonForIncompletion()).append(" "); + final String failureReasons = + joinOn.stream() + .map(workflow::getTaskByRefName) + .filter(t -> !t.getStatus().isSuccessful()) + .map(TaskModel::getReasonForIncompletion) + .collect(Collectors.joining(" ")); + failureReason.append(failureReasons); } break; diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java index e0f7be9fa..14fdba6f1 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java @@ -23,6 +23,7 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; +import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_JOIN; @Component(TASK_TYPE_JOIN) @@ -57,9 +58,21 @@ public boolean execute( break; } TaskModel.Status taskStatus = forkedTask.getStatus(); - hasFailures = !taskStatus.isSuccessful() && !forkedTask.getWorkflowTask().isOptional(); + hasFailures = + !taskStatus.isSuccessful() + && !forkedTask.getWorkflowTask().isOptional() + && (!PERMISSIVE.name().equals(forkedTask.getWorkflowTask().getType()) + || joinOn.stream() + .map(workflow::getTaskByRefName) + .allMatch(t -> t.getStatus().isTerminal())); if (hasFailures) { - failureReason.append(forkedTask.getReasonForIncompletion()).append(" "); + final String failureReasons = + joinOn.stream() + .map(workflow::getTaskByRefName) + .filter(t -> !t.getStatus().isSuccessful()) + .map(TaskModel::getReasonForIncompletion) + .collect(Collectors.joining(" ")); + failureReason.append(failureReasons); } // Only add to task output if it's not empty if (!forkedTask.getOutputData().isEmpty()) { diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java index dbf0277f6..ca06c8ea8 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java @@ -440,6 +440,80 @@ public void testOptional() { outcome.tasksToBeScheduled.get(0).getReferenceTaskName()); } + /** Similar to {@link #testOptional} */ + @Test + public void testPermissive() { + WorkflowDef def = new WorkflowDef(); + def.setName("test-permissive"); + + WorkflowTask task1 = new WorkflowTask(); + task1.setName("task0"); + task1.setType("PERMISSIVE"); + task1.setTaskReferenceName("t0"); + task1.getInputParameters().put("taskId", "${CPEWF_TASK_ID}"); + task1.setTaskDefinition(new TaskDef("task0")); + + WorkflowTask task2 = new WorkflowTask(); + task2.setName("task1"); + task2.setType("PERMISSIVE"); + task2.setTaskReferenceName("t1"); + task2.setTaskDefinition(new TaskDef("task1")); + + def.getTasks().add(task1); + def.getTasks().add(task2); + def.setSchemaVersion(2); + + WorkflowModel workflow = new WorkflowModel(); + workflow.setWorkflowDefinition(def); + workflow.setCreateTime(System.currentTimeMillis()); + DeciderOutcome outcome = deciderService.decide(workflow); + assertNotNull(outcome); + assertEquals(1, outcome.tasksToBeScheduled.size()); + assertEquals( + task1.getTaskReferenceName(), + outcome.tasksToBeScheduled.get(0).getReferenceTaskName()); + + for (int i = 0; i < 3; i++) { + String task1Id = outcome.tasksToBeScheduled.get(0).getTaskId(); + assertEquals(task1Id, outcome.tasksToBeScheduled.get(0).getInputData().get("taskId")); + + workflow.getTasks().clear(); + workflow.getTasks().addAll(outcome.tasksToBeScheduled); + workflow.getTasks().get(0).setStatus(TaskModel.Status.FAILED); + + outcome = deciderService.decide(workflow); + + assertNotNull(outcome); + assertEquals(1, outcome.tasksToBeUpdated.size()); + assertEquals(1, outcome.tasksToBeScheduled.size()); + + assertEquals(TaskModel.Status.FAILED, workflow.getTasks().get(0).getStatus()); + assertEquals(task1Id, outcome.tasksToBeUpdated.get(0).getTaskId()); + assertEquals( + task1.getTaskReferenceName(), + outcome.tasksToBeScheduled.get(0).getReferenceTaskName()); + assertEquals(i + 1, outcome.tasksToBeScheduled.get(0).getRetryCount()); + } + + String task1Id = outcome.tasksToBeScheduled.get(0).getTaskId(); + + workflow.getTasks().clear(); + workflow.getTasks().addAll(outcome.tasksToBeScheduled); + workflow.getTasks().get(0).setStatus(TaskModel.Status.FAILED); + + outcome = deciderService.decide(workflow); + + assertNotNull(outcome); + assertEquals(1, outcome.tasksToBeUpdated.size()); + assertEquals(1, outcome.tasksToBeScheduled.size()); + + assertEquals(TaskModel.Status.FAILED, workflow.getTasks().get(0).getStatus()); + assertEquals(task1Id, outcome.tasksToBeUpdated.get(0).getTaskId()); + assertEquals( + task2.getTaskReferenceName(), + outcome.tasksToBeScheduled.get(0).getReferenceTaskName()); + } + @Test public void testOptionalWithDynamicFork() { WorkflowDef def = new WorkflowDef(); diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java new file mode 100644 index 000000000..97920a3f0 --- /dev/null +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.core.execution.mapper; + +import java.util.HashMap; +import java.util.List; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.core.exception.TerminateWorkflowException; +import com.netflix.conductor.core.utils.IDGenerator; +import com.netflix.conductor.core.utils.ParametersUtils; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.*; + +public class PermissiveTaskMapperTest { + + private PermissiveTaskMapper permissiveTaskMapper; + + private IDGenerator idGenerator = new IDGenerator(); + + @Rule public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + ParametersUtils parametersUtils = mock(ParametersUtils.class); + permissiveTaskMapper = new PermissiveTaskMapper(parametersUtils); + } + + @Test + public void getMappedTasks() { + + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName("permissive_task"); + workflowTask.setTaskDefinition(new TaskDef("permissive_task")); + + String taskId = idGenerator.generate(); + String retriedTaskId = idGenerator.generate(); + + WorkflowDef workflowDef = new WorkflowDef(); + WorkflowModel workflow = new WorkflowModel(); + workflow.setWorkflowDefinition(workflowDef); + + TaskMapperContext taskMapperContext = + TaskMapperContext.newBuilder() + .withWorkflowModel(workflow) + .withTaskDefinition(new TaskDef()) + .withWorkflowTask(workflowTask) + .withTaskInput(new HashMap<>()) + .withRetryCount(0) + .withRetryTaskId(retriedTaskId) + .withTaskId(taskId) + .build(); + + List mappedTasks = permissiveTaskMapper.getMappedTasks(taskMapperContext); + assertNotNull(mappedTasks); + assertEquals(1, mappedTasks.size()); + } + + @Test + public void getMappedTasksException() { + + // Given + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName("permissive_task"); + String taskId = idGenerator.generate(); + String retriedTaskId = idGenerator.generate(); + + WorkflowDef workflowDef = new WorkflowDef(); + WorkflowModel workflow = new WorkflowModel(); + workflow.setWorkflowDefinition(workflowDef); + + TaskMapperContext taskMapperContext = + TaskMapperContext.newBuilder() + .withWorkflowModel(workflow) + .withTaskDefinition(new TaskDef()) + .withWorkflowTask(workflowTask) + .withTaskInput(new HashMap<>()) + .withRetryCount(0) + .withRetryTaskId(retriedTaskId) + .withTaskId(taskId) + .build(); + + // then + expectedException.expect(TerminateWorkflowException.class); + expectedException.expectMessage( + String.format( + "Invalid task. Task %s does not have a definition", + workflowTask.getName())); + + // when + permissiveTaskMapper.getMappedTasks(taskMapperContext); + } +} diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java new file mode 100644 index 000000000..f767f8ea5 --- /dev/null +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +/* Workflow permissive task executed by a worker */ +public class PermissiveTask extends Task { + + private TaskDef taskDef; + + public PermissiveTask(String taskDefName, String taskReferenceName) { + super(taskReferenceName, TaskType.PERMISSIVE); + super.name(taskDefName); + } + + PermissiveTask(WorkflowTask workflowTask) { + super(workflowTask); + this.taskDef = workflowTask.getTaskDefinition(); + } + + public TaskDef getTaskDef() { + return taskDef; + } + + public PermissiveTask setTaskDef(TaskDef taskDef) { + this.taskDef = taskDef; + return this; + } + + @Override + protected void updateWorkflowTask(WorkflowTask workflowTask) { + workflowTask.setTaskDefinition(taskDef); + } +} diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java index ae75b6c83..6601f1a23 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -72,6 +72,7 @@ public static void initTaskImplementations() { TaskRegistry.register(TaskType.DYNAMIC.name(), Dynamic.class); TaskRegistry.register(TaskType.FORK_JOIN_DYNAMIC.name(), DynamicFork.class); TaskRegistry.register(TaskType.FORK_JOIN.name(), ForkJoin.class); + TaskRegistry.register(TaskType.PERMISSIVE.name(), PermissiveTask.class); TaskRegistry.register(TaskType.HTTP.name(), Http.class); TaskRegistry.register(TaskType.INLINE.name(), Javascript.class); TaskRegistry.register(TaskType.JOIN.name(), Join.class); diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy index 437f4e1cc..b6540b160 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy @@ -16,11 +16,11 @@ import org.springframework.beans.factory.annotation.Autowired import com.netflix.conductor.common.metadata.tasks.Task import com.netflix.conductor.common.metadata.tasks.TaskDef +import com.netflix.conductor.common.metadata.tasks.TaskType import com.netflix.conductor.common.metadata.workflow.RerunWorkflowRequest import com.netflix.conductor.common.run.Workflow import com.netflix.conductor.core.execution.tasks.Join import com.netflix.conductor.core.execution.tasks.SubWorkflow -import com.netflix.conductor.dao.QueueDAO import com.netflix.conductor.test.base.AbstractSpecification import spock.lang.Shared @@ -45,18 +45,23 @@ class ForkJoinSpec extends AbstractSpecification { @Shared def FORK_JOIN_SUB_WORKFLOW = 'integration_test_fork_join_sw' + @Shared + def FORK_JOIN_PERMISSIVE_WF = 'FanInOutPermissiveTest' + @Autowired SubWorkflow subWorkflowTask def setup() { workflowTestUtil.registerWorkflows('fork_join_integration_test.json', 'fork_join_with_no_task_retry_integration_test.json', + 'fork_join_with_no_permissive_task_retry_integration_test.json', 'nested_fork_join_integration_test.json', 'simple_workflow_1_integration_test.json', 'nested_fork_join_with_sub_workflow_integration_test.json', 'simple_one_task_sub_workflow_integration_test.json', 'fork_join_with_optional_sub_workflow_forks_integration_test.json', - 'fork_join_sub_workflow.json' + 'fork_join_sub_workflow.json', + 'fork_join_permissive_integration_test.json', ) } @@ -251,6 +256,110 @@ class ForkJoinSpec extends AbstractSpecification { metadataService.updateTaskDef(persistedIntegrationTask2Definition) } + /** + * start + * | + * fork + * / \ + * p_task1 p_task2 + * | / + * \ / + * \ / + * join + * | + * s_task3 + * | + * End + */ + def "Test a simple workflow with fork join permissive failure flow"() { + setup: "Ensure that 'integration_task_1' has a retry count of 0" + def persistedIntegrationTask1Definition = workflowTestUtil.getPersistedTaskDefinition('integration_task_1').get() + def modifiedIntegrationTask1Definition = new TaskDef(persistedIntegrationTask1Definition.name, + persistedIntegrationTask1Definition.description, persistedIntegrationTask1Definition.ownerEmail, 0, + 0, persistedIntegrationTask1Definition.responseTimeoutSeconds) + metadataService.updateTaskDef(modifiedIntegrationTask1Definition) + + when: "A fork join workflow is started" + def workflowInstanceId = startWorkflow(FORK_JOIN_PERMISSIVE_WF, 1, + 'fanoutTest', [:], + null) + + then: "verify that the workflow has started and the starting nodes of the each fork are in scheduled state" + workflowInstanceId + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 4 + tasks[0].status == Task.Status.COMPLETED + tasks[0].taskType == 'FORK' + tasks[1].workflowTask.type == TaskType.PERMISSIVE.name() + tasks[1].status == Task.Status.SCHEDULED + tasks[1].taskType == 'integration_task_1' + tasks[2].workflowTask.type == TaskType.PERMISSIVE.name() + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_task_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + } + + when: "The first task of the fork is polled and completed" + def joinTaskId = workflowExecutionService.getExecutionStatus(workflowInstanceId, true).getTaskByRefName("fanouttask_join").getTaskId() + def polledAndAckTask1Try1 = workflowTestUtil.pollAndFailTask('integration_task_1', 'task1.worker', 'Failed...') + + then: "verify that the 'integration_task_1' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask1Try1) + + and: "The workflow has been updated and has all the required tasks in the right status to move forward" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 4 + tasks[1].status == Task.Status.FAILED + tasks[1].taskType == 'integration_task_1' + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_task_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + } + + when: "The other node of the fork is completed by completing 'integration_task_2'" + def polledAndAckTask2Try1 = workflowTestUtil.pollAndCompleteTask('integration_task_2','task1.worker') + + and: "workflow is evaluated" + sweep(workflowInstanceId) + + then: "verify that the 'integration_task_2' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask2Try1) + + and: "the workflow is in the failed state" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 4 + tasks[1].status == Task.Status.FAILED + tasks[1].taskType == 'integration_task_1' + tasks[2].status == Task.Status.COMPLETED + tasks[2].taskType == 'integration_task_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + } + + when: "JOIN task executed by the async executor" + asyncSystemTaskExecutor.execute(joinTask, joinTaskId) + + then: "The workflow has been updated with the task status and task list" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.FAILED + tasks.size() == 4 + tasks[1].status == Task.Status.FAILED + tasks[1].taskType == 'integration_task_1' + tasks[2].status == Task.Status.COMPLETED + tasks[2].taskType == 'integration_task_2' + tasks[3].status == Task.Status.FAILED + tasks[3].taskType == 'JOIN' + } + + cleanup: "Restore the task definitions that were modified as part of this feature testing" + metadataService.updateTaskDef(persistedIntegrationTask1Definition) + } + def "Test retrying a failed fork join workflow"() { when: "A fork join workflow is started" @@ -384,6 +493,166 @@ class ForkJoinSpec extends AbstractSpecification { } } + def "Test retrying a failed permissive fork join workflow"() { + + when: "A fork join permissive workflow is started" + def workflowInstanceId = startWorkflow(FORK_JOIN_PERMISSIVE_WF + '_2', 1, + 'fanoutTest', [:], + null) + + then: "verify that the workflow has started and the starting nodes of the each fork are in scheduled state" + workflowInstanceId + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 4 + tasks[0].status == Task.Status.COMPLETED + tasks[0].taskType == 'FORK' + tasks[1].status == Task.Status.SCHEDULED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + } + + when: "The first task of the fork is polled and completed" + def joinTaskId = workflowExecutionService.getExecutionStatus(workflowInstanceId, true).getTaskByRefName("fanouttask_join").getTaskId() + def polledAndAckTask1Try1 = workflowTestUtil.pollAndCompleteTask('integration_p_task_0_RT_1', 'task1.worker') + + then: "verify that the 'integration_task_p_0_RT_1' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask1Try1) + + and: "The workflow has been updated and has all the required tasks in the right status to move forward" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 5 + tasks[1].status == Task.Status.COMPLETED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + tasks[4].status == Task.Status.SCHEDULED + tasks[4].taskType == 'integration_p_task_0_RT_3' + } + + when: "The other node of the fork is completed by completing 'integration_p_task_0_RT_2'" + def polledAndAckTask2Try1 = workflowTestUtil.pollAndFailTask('integration_p_task_0_RT_2', + 'task1.worker', 'Failed....') + + and: "workflow is evaluated" + sweep(workflowInstanceId) + + then: "verify that the 'integration_p_task_0_RT_2' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask2Try1) + + and: "the workflow is not in the failed state, until the completion of the permissive forked tasks" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 5 + tasks[1].status == Task.Status.COMPLETED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.FAILED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + tasks[4].status == Task.Status.SCHEDULED + tasks[4].taskType == 'integration_p_task_0_RT_3' + } + + when: "The other node of the fork is completed by completing 'integration_p_task_0_RT_3'" + def polledAndAckTask3Try1 = workflowTestUtil.pollAndFailTask('integration_p_task_0_RT_3', + 'task1.worker', 'Failed....') + + and: "workflow is evaluated" + sweep(workflowInstanceId) + + then: "verify that the 'integration_p_task_0_RT_3' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask3Try1) + + and: "JOIN task is polled and executed" + asyncSystemTaskExecutor.execute(joinTask, joinTaskId) + + and: "the workflow is in the failed state" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.FAILED + tasks.size() == 5 + tasks[1].status == Task.Status.COMPLETED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.FAILED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.FAILED + tasks[3].taskType == 'JOIN' + tasks[4].status == Task.Status.FAILED + tasks[4].taskType == 'integration_p_task_0_RT_3' + } + + when: "The workflow is retried" + workflowExecutor.retry(workflowInstanceId, false) + + then: "verify that all the workflow is retried and new tasks are added in place of the failed tasks" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 7 + tasks[1].status == Task.Status.COMPLETED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.FAILED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.IN_PROGRESS + tasks[3].taskType == 'JOIN' + tasks[4].status == Task.Status.FAILED + tasks[4].taskType == 'integration_p_task_0_RT_3' + tasks[5].status == Task.Status.SCHEDULED + tasks[5].taskType == 'integration_p_task_0_RT_2' + tasks[6].status == Task.Status.SCHEDULED + tasks[6].taskType == 'integration_p_task_0_RT_3' + } + + when: "The 'integration_p_task_0_RT_3' is polled and completed" + def polledAndAckTask3Try2 = workflowTestUtil.pollAndCompleteTask('integration_p_task_0_RT_3', 'task1.worker') + + then: "verify that the 'integration_p_task_3' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask3Try2) + + when: "The other node of the fork is completed by completing 'integration_p_task_0_RT_2'" + def polledAndAckTask2Try2 = workflowTestUtil.pollAndCompleteTask('integration_p_task_0_RT_2', 'task1.worker') + + and: "workflow is evaluated" + sweep(workflowInstanceId) + + then: "verify that the 'integration_p_task_2' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask2Try2) + + when: "JOIN task is polled and executed" + asyncSystemTaskExecutor.execute(joinTask, joinTaskId) + + and: "The last task of the workflow is then polled and completed integration_p_task_0_RT_4'" + def polledAndAckTask4Try1 = workflowTestUtil.pollAndCompleteTask('integration_p_task_0_RT_4', 'task1.worker') + + then: "verify that the 'integration_p_task_0_RT_4' was polled and acknowledged" + workflowTestUtil.verifyPolledAndAcknowledgedTask(polledAndAckTask4Try1) + + then: "Then verify that the workflow is completed and the task list of execution is as expected" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.COMPLETED + tasks.size() == 8 + tasks[1].status == Task.Status.COMPLETED + tasks[1].taskType == 'integration_p_task_0_RT_1' + tasks[2].status == Task.Status.FAILED + tasks[2].taskType == 'integration_p_task_0_RT_2' + tasks[3].status == Task.Status.COMPLETED + tasks[3].taskType == 'JOIN' + tasks[4].status == Task.Status.FAILED + tasks[4].taskType == 'integration_p_task_0_RT_3' + tasks[5].status == Task.Status.COMPLETED + tasks[5].taskType == 'integration_p_task_0_RT_2' + tasks[6].status == Task.Status.COMPLETED + tasks[6].taskType == 'integration_p_task_0_RT_3' + tasks[7].status == Task.Status.COMPLETED + tasks[7].taskType == 'integration_p_task_0_RT_4' + } + } + def "Test nested fork join workflow success flow"() { given: "Input for the nested fork join workflow" Map input = new HashMap() diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy index 0fbf0ec0b..53d5312df 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy @@ -45,6 +45,12 @@ class WorkflowAndTaskConfigurationSpec extends AbstractSpecification { @Shared def WORKFLOW_WITH_OPTIONAL_TASK = 'optional_task_wf' + @Shared + def WORKFLOW_WITH_PERMISSIVE_TASK = 'permissive_task_wf' + + @Shared + def WORKFLOW_WITH_PERMISSIVE_OPTIONAL_TASK = 'permissive_optional_task_wf' + @Shared def TEST_WORKFLOW = 'integration_test_wf3' @@ -52,12 +58,14 @@ class WorkflowAndTaskConfigurationSpec extends AbstractSpecification { def WAIT_TIME_OUT_WORKFLOW = 'test_wait_timeout' def setup() { - //Register LINEAR_WORKFLOW_T1_T2, TEST_WORKFLOW, RTOWF, WORKFLOW_WITH_OPTIONAL_TASK + //Register LINEAR_WORKFLOW_T1_T2, TEST_WORKFLOW, RTOWF, WORKFLOW_WITH_OPTIONAL_TASK, WORKFLOW_WITH_PERMISSIVE_TASK, WORKFLOW_WITH_PERMISSIVE_OPTIONAL_TASK workflowTestUtil.registerWorkflows( 'simple_workflow_1_integration_test.json', 'simple_workflow_1_input_template_integration_test.json', 'simple_workflow_3_integration_test.json', 'simple_workflow_with_optional_task_integration_test.json', + 'simple_workflow_with_permissive_task_integration_test.json', + 'simple_workflow_with_permissive_optional_task_integration_test.json', 'simple_wait_task_workflow_integration_test.json') } @@ -133,6 +141,155 @@ class WorkflowAndTaskConfigurationSpec extends AbstractSpecification { } } + def "Test simple workflow which has a permissive task"() { + + given: "A input parameters for a workflow with a permissive task" + def correlationId = 'integration_test' + UUID.randomUUID().toString() + def workflowInput = new HashMap() + workflowInput['param1'] = 'p1 value' + workflowInput['param2'] = 'p2 value' + + when: "A permissive task workflow is started" + def workflowInstanceId = startWorkflow(WORKFLOW_WITH_PERMISSIVE_TASK, 1, + correlationId, workflowInput, + null) + + then: "verify that the workflow has started and the permissive task is in a scheduled state" + workflowInstanceId + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 1 + tasks[0].status == Task.Status.SCHEDULED + tasks[0].taskType == 'task_permissive' + } + + when: "The first permissive task is polled and failed" + Tuple polledAndFailedTaskTry1 = workflowTestUtil.pollAndFailTask('task_permissive', + 'task1.integration.worker', 'NETWORK ERROR') + + then: "Verify that the task_permissive was polled and acknowledged" + verifyPolledAndAcknowledgedTask(polledAndFailedTaskTry1) + + when: "A decide is executed on the workflow" + workflowExecutor.decide(workflowInstanceId) + + then: "verify that the workflow is still running and the first permissive task has failed and the retry has kicked in" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 2 + tasks[0].status == Task.Status.FAILED + tasks[0].taskType == 'task_permissive' + tasks[1].status == Task.Status.SCHEDULED + tasks[1].taskType == 'task_permissive' + } + + when: "The first permissive task is polled and failed" + Tuple polledAndFailedTaskTry2 = workflowTestUtil.pollAndFailTask('task_permissive', + 'task1.integration.worker', 'NETWORK ERROR') + + then: "Verify that the task_permissive was polled and acknowledged" + verifyPolledAndAcknowledgedTask(polledAndFailedTaskTry2) + + workflowExecutor.decide(workflowInstanceId) + + then: "Ensure that the workflow is updated" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 3 + tasks[1].status == Task.Status.FAILED + tasks[1].taskType == 'task_permissive' + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_task_2' + } + + when: "The second task 'integration_task_2' is polled and completed" + def task2Try1 = workflowTestUtil.pollAndCompleteTask('integration_task_2', 'task2.integration.worker') + + then: "Verify that the task was polled and acknowledged" + verifyPolledAndAcknowledgedTask(task2Try1) + + and: "Ensure that the workflow is in completed state" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.FAILED + reasonForIncompletion == "Task ${tasks[1].taskId} failed with status: FAILED and reason: 'NETWORK ERROR'" + tasks.size() == 3 + tasks[2].status == Task.Status.COMPLETED + tasks[2].taskType == 'integration_task_2' + } + } + + def "Test simple workflow which has a permissive optional task"() { + + given: "A input parameters for a workflow with a permissive optional task" + def correlationId = 'integration_test' + UUID.randomUUID().toString() + def workflowInput = new HashMap() + workflowInput['param1'] = 'p1 value' + workflowInput['param2'] = 'p2 value' + + when: "A permissive optional task workflow is started" + def workflowInstanceId = startWorkflow(WORKFLOW_WITH_PERMISSIVE_OPTIONAL_TASK, 1, + correlationId, workflowInput, + null) + + then: "verify that the workflow has started and the permissive optional task is in a scheduled state" + workflowInstanceId + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 1 + tasks[0].status == Task.Status.SCHEDULED + tasks[0].taskType == 'task_optional' + } + + when: "The first permissive optional task is polled and failed" + Tuple polledAndFailedTaskTry1 = workflowTestUtil.pollAndFailTask('task_optional', + 'task1.integration.worker', 'NETWORK ERROR') + + then: "Verify that the task_optional was polled and acknowledged" + verifyPolledAndAcknowledgedTask(polledAndFailedTaskTry1) + + when: "A decide is executed on the workflow" + workflowExecutor.decide(workflowInstanceId) + + then: "verify that the workflow is still running and the first permissive optional task has failed and the retry has kicked in" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 2 + tasks[0].status == Task.Status.FAILED + tasks[0].taskType == 'task_optional' + tasks[1].status == Task.Status.SCHEDULED + tasks[1].taskType == 'task_optional' + } + + when: "Poll the permissive optional task again and do not complete it and run decide" + workflowExecutionService.poll('task_optional', 'task1.integration.worker') + Thread.sleep(5000) + workflowExecutor.decide(workflowInstanceId) + + then: "Ensure that the workflow is updated" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.RUNNING + tasks.size() == 3 + tasks[1].status == Task.Status.COMPLETED_WITH_ERRORS + tasks[1].taskType == 'task_optional' + tasks[2].status == Task.Status.SCHEDULED + tasks[2].taskType == 'integration_task_2' + } + + when: "The second task 'integration_task_2' is polled and completed" + def task2Try1 = workflowTestUtil.pollAndCompleteTask('integration_task_2', 'task2.integration.worker') + + then: "Verify that the task was polled and acknowledged" + verifyPolledAndAcknowledgedTask(task2Try1) + + and: "Ensure that the workflow is in completed state" + with(workflowExecutionService.getExecutionStatus(workflowInstanceId, true)) { + status == Workflow.WorkflowStatus.COMPLETED + tasks.size() == 3 + tasks[2].status == Task.Status.COMPLETED + tasks[2].taskType == 'integration_task_2' + } + } + def "test workflow with input template parsing"() { given: "Input parameters for a workflow with input template" def correlationId = 'integration_test' + UUID.randomUUID().toString() diff --git a/test-harness/src/test/resources/fork_join_permissive_integration_test.json b/test-harness/src/test/resources/fork_join_permissive_integration_test.json new file mode 100644 index 000000000..6f65d4e11 --- /dev/null +++ b/test-harness/src/test/resources/fork_join_permissive_integration_test.json @@ -0,0 +1,109 @@ +{ + "name": "FanInOutPermissiveTest", + "description": "FanInOutPermissiveTest", + "version": 1, + "tasks": [ + { + "name": "fork", + "taskReferenceName": "fanouttask", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "integration_task_1", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "workflow.input.param1", + "p2": "workflow.input.param2" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + [ + { + "name": "integration_task_2", + "taskReferenceName": "t2", + "inputParameters": { + "tp1": "workflow.input.param1" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + ], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "join", + "taskReferenceName": "fanouttask_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [ + "t1", + "t2" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "integration_task_3", + "taskReferenceName": "t3", + "inputParameters": { + "p1": "workflow.input.param1", + "p2": "workflow.input.param2" + }, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file diff --git a/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json b/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json new file mode 100644 index 000000000..8c1ebef77 --- /dev/null +++ b/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json @@ -0,0 +1,190 @@ +{ + "name": "FanInOutPermissiveTest_2", + "description": "FanInOutPermissiveTest_2", + "version": 1, + "tasks": [ + { + "name": "fork", + "taskReferenceName": "fanouttask", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "integration_p_task_0_RT_1", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "workflow.input.param1", + "p2": "workflow.input.param2" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "createdBy": "integration_app", + "name": "integration_p_task_0_RT_1", + "description": "integration_p_task_0_RT_1", + "retryCount": 0, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + } + }, + { + "name": "integration_p_task_0_RT_3", + "taskReferenceName": "t3", + "inputParameters": { + "p1": "workflow.input.param1", + "p2": "workflow.input.param2" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "createdBy": "integration_app", + "name": "integration_p_task_0_RT_3", + "description": "integration_p_task_0_RT_3", + "retryCount": 0, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + } + } + ], + [ + { + "name": "integration_p_task_0_RT_2", + "taskReferenceName": "t2", + "inputParameters": { + "tp1": "workflow.input.param1" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "createdBy": "integration_app", + "name": "integration_p_task_0_RT_2", + "description": "integration_p_task_0_RT_2", + "retryCount": 0, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + } + } + ] + ], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "join", + "taskReferenceName": "fanouttask_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [ + "t3", + "t2" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "integration_p_task_0_RT_4", + "taskReferenceName": "t4", + "inputParameters": { + "tp1": "workflow.input.param1" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "createdBy": "integration_app", + "name": "integration_p_task_0_RT_4", + "description": "integration_p_task_0_RT_4", + "retryCount": 0, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + } + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file diff --git a/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json b/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json new file mode 100644 index 000000000..84c6910ab --- /dev/null +++ b/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json @@ -0,0 +1,58 @@ +{ + "name": "permissive_optional_task_wf", + "description": "permissive_optional_task_wf", + "version": 1, + "tasks": [ + { + "name": "task_optional", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "${workflow.input.param1}", + "p2": "${workflow.input.param2}" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": true, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "integration_task_2", + "taskReferenceName": "t2", + "inputParameters": { + "tp1": "${workflow.input.param1}", + "tp2": "${t1.output.op}" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "outputParameters": { + "o1": "${workflow.input.param1}", + "o2": "${t2.output.uuid}", + "o3": "${t1.output.op}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file diff --git a/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json b/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json new file mode 100644 index 000000000..2b8f4dbfe --- /dev/null +++ b/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json @@ -0,0 +1,92 @@ +{ + "name": "permissive_task_wf", + "description": "permissive_task_wf", + "version": 1, + "tasks": [ + { + "name": "task_permissive", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "${workflow.input.param1}", + "p2": "${workflow.input.param2}" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "retryCount": 1, + "taskDefinition": { + "createdBy": "integration_app", + "name": "task_permissive", + "description": "task_permissive", + "retryCount": 1, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + }, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "integration_task_2", + "taskReferenceName": "t2", + "inputParameters": { + "tp1": "${workflow.input.param1}", + "tp2": "${t1.output.op}" + }, + "type": "PERMISSIVE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "retryCount": 1, + "taskDefinition": { + "createdBy": "integration_app", + "name": "integration_task_2", + "description": "integration_task_2", + "retryCount": 1, + "timeoutSeconds": 120, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + }, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "outputParameters": { + "o1": "${workflow.input.param1}", + "o2": "${t2.output.uuid}", + "o3": "${t1.output.op}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file From 4fe1aaeb179d65fffdd4614edbabef20beaff84a Mon Sep 17 00:00:00 2001 From: mallain Date: Thu, 14 Dec 2023 13:18:14 +0100 Subject: [PATCH 024/202] bugfix: wrong unit used in SetVariable error message on payloadSize > maxThreshold --- .../com/netflix/conductor/core/execution/tasks/SetVariable.java | 2 +- .../conductor/test/integration/SetVariableTaskSpec.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java index 8353e6a2e..2af835cdd 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java @@ -66,7 +66,7 @@ private boolean validateVariablesSize( if (payloadSize > maxThreshold * 1024) { String errorMsg = String.format( - "The variables payload size: %d of workflow: %s is greater than the permissible limit: %d bytes", + "The variables payload size: %d of workflow: %s is greater than the permissible limit: %d kilobytes", payloadSize, workflowId, maxThreshold); LOGGER.error(errorMsg); task.setReasonForIncompletion(errorMsg); diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy index 5eace9207..714300f48 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy @@ -62,7 +62,7 @@ class SetVariableTaskSpec extends AbstractSpecification { def EXTRA_HASHMAP_SIZE = 17 def expectedErrorMessage = String.format( - "The variables payload size: %d of workflow: %s is greater than the permissible limit: %d bytes", + "The variables payload size: %d of workflow: %s is greater than the permissible limit: %d kilobytes", EXTRA_HASHMAP_SIZE + maxThreshold * 1024 + 1, workflowInstanceId, maxThreshold) then: "verify that the task is completed and variables were set" From 2bd645cd33e00fb6734b3854fc5a2f554b4bb71d Mon Sep 17 00:00:00 2001 From: Scott Carter Date: Thu, 14 Dec 2023 10:22:28 -0500 Subject: [PATCH 025/202] Resolve open CVEs in Conductor code. --- annotations-processor/dependencies.lock | 98 +++---- annotations/dependencies.lock | 98 +++---- awss3-storage/dependencies.lock | 108 ++++---- awssqs-event-queue/dependencies.lock | 112 ++++---- build.gradle | 68 ++--- cassandra-persistence/dependencies.lock | 100 +++---- client-spring/dependencies.lock | 110 ++++---- client/dependencies.lock | 106 +++---- common/dependencies.lock | 106 +++---- core/dependencies.lock | 106 +++---- dependencies.gradle | 6 +- dependencies.lock | 98 +++---- es6-persistence/dependencies.lock | 100 +++---- grpc-client/dependencies.lock | 122 ++++---- grpc-server/dependencies.lock | 132 ++++----- grpc/dependencies.lock | 172 ++++++------ http-task/dependencies.lock | 106 +++---- java-sdk/dependencies.lock | 106 +++---- json-jq-task/dependencies.lock | 100 +++---- redis-concurrency-limit/dependencies.lock | 106 +++---- redis-lock/dependencies.lock | 100 +++---- redis-persistence/dependencies.lock | 100 +++---- rest/dependencies.lock | 106 +++---- server/dependencies.lock | 324 ++++++++-------------- test-harness/dependencies.lock | 164 +++++------ 25 files changed, 1355 insertions(+), 1499 deletions(-) diff --git a/annotations-processor/dependencies.lock b/annotations-processor/dependencies.lock index 1c9fbc411..5e33e8d46 100644 --- a/annotations-processor/dependencies.lock +++ b/annotations-processor/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.jknack:handlebars": { "locked": "4.3.1" @@ -192,67 +192,67 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.jknack:handlebars": { "locked": "4.3.1" @@ -311,37 +311,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.jknack:handlebars": { "locked": "4.3.1" @@ -386,10 +386,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -400,67 +400,67 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.jknack:handlebars": { "locked": "4.3.1" @@ -520,10 +520,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/annotations/dependencies.lock b/annotations/dependencies.lock index 2e6c00bb1..3869be58c 100644 --- a/annotations/dependencies.lock +++ b/annotations/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -59,37 +59,37 @@ }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -112,37 +112,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "junit:junit": { "locked": "4.13.2" @@ -169,10 +169,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -180,37 +180,37 @@ }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "junit:junit": { "locked": "4.13.2" @@ -237,10 +237,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" diff --git a/awss3-storage/dependencies.lock b/awss3-storage/dependencies.lock index 311ecc794..1d8630110 100644 --- a/awss3-storage/dependencies.lock +++ b/awss3-storage/dependencies.lock @@ -1,45 +1,45 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.amazonaws:aws-java-sdk-s3": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -66,7 +66,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -74,7 +74,7 @@ }, "runtimeClasspath": { "com.amazonaws:aws-java-sdk-s3": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -82,7 +82,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -90,7 +90,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -98,7 +98,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -106,7 +106,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -114,7 +114,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -122,7 +122,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -130,7 +130,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -138,7 +138,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -146,7 +146,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -154,7 +154,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -162,7 +162,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -305,40 +305,40 @@ }, "testCompileClasspath": { "com.amazonaws:aws-java-sdk-s3": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -374,10 +374,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -385,7 +385,7 @@ }, "testRuntimeClasspath": { "com.amazonaws:aws-java-sdk-s3": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -393,7 +393,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -401,7 +401,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -409,7 +409,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -417,7 +417,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -425,7 +425,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -433,7 +433,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -441,7 +441,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -449,7 +449,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -457,7 +457,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -465,7 +465,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -473,7 +473,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -615,10 +615,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/awssqs-event-queue/dependencies.lock b/awssqs-event-queue/dependencies.lock index 3fe9d6ca9..f93e7756f 100644 --- a/awssqs-event-queue/dependencies.lock +++ b/awssqs-event-queue/dependencies.lock @@ -1,45 +1,45 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.amazonaws:aws-java-sdk-sqs": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -72,7 +72,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -80,7 +80,7 @@ }, "runtimeClasspath": { "com.amazonaws:aws-java-sdk-sqs": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -88,7 +88,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -96,7 +96,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -104,7 +104,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -112,7 +112,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -120,7 +120,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -128,7 +128,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -136,7 +136,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -144,7 +144,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -152,7 +152,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -160,7 +160,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -168,7 +168,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -314,40 +314,40 @@ }, "testCompileClasspath": { "com.amazonaws:aws-java-sdk-sqs": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -389,13 +389,13 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -403,7 +403,7 @@ }, "testRuntimeClasspath": { "com.amazonaws:aws-java-sdk-sqs": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -411,7 +411,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -419,7 +419,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -427,7 +427,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -435,7 +435,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -451,7 +451,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -459,7 +459,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -475,7 +475,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -483,7 +483,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -491,7 +491,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -636,13 +636,13 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/build.gradle b/build.gradle index d99542949..12ceb391f 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:10.0.0' - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.7.16' + classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.7.18' classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.+' } } @@ -159,89 +159,89 @@ allprojects { implementation('com.fasterxml.jackson.core:jackson-core') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.core:jackson-databind') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.core:jackson-annotations') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.dataformat:jackson-dataformat-smile') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.datatype:jackson-datatype-jdk8') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.datatype:jackson-datatype-joda') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.datatype:jackson-datatype-jsr310') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('com.fasterxml.jackson.module:jackson-module-afterburner') { version { // this is the preferred version this library will use - prefer '2.15.0' - // the strict bounds, effectively allowing any 2.15.x version between 2.15.0 and 2.15.2 - strictly '[2.15.0,2.15.2)' + prefer '2.15.3' + // the strict bounds, effectively allowing any 2.15.3 or higher + strictly '[2.15.3,2.15.3+]' } } implementation('org.apache.logging.log4j:log4j-core') diff --git a/cassandra-persistence/dependencies.lock b/cassandra-persistence/dependencies.lock index 18ff44084..602532ed2 100644 --- a/cassandra-persistence/dependencies.lock +++ b/cassandra-persistence/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { @@ -9,37 +9,37 @@ "locked": "3.10.2" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -66,7 +66,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -82,7 +82,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -90,7 +90,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -98,7 +98,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -106,7 +106,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -114,7 +114,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -122,7 +122,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -130,7 +130,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -138,7 +138,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -146,7 +146,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -154,7 +154,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -162,7 +162,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -308,37 +308,37 @@ "locked": "3.10.2" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -386,10 +386,10 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:cassandra": { "locked": "1.19.1" @@ -411,7 +411,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -419,7 +419,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -427,7 +427,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -435,7 +435,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -451,7 +451,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -459,7 +459,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -475,7 +475,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -483,7 +483,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -491,7 +491,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -642,10 +642,10 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:cassandra": { "locked": "1.19.1" diff --git a/client-spring/dependencies.lock b/client-spring/dependencies.lock index 9602d6246..e6ab52ab9 100644 --- a/client-spring/dependencies.lock +++ b/client-spring/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-client": { "project": true @@ -66,7 +66,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -83,7 +83,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -92,7 +92,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -101,7 +101,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -110,7 +110,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -119,7 +119,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -128,7 +128,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -137,7 +137,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -146,7 +146,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -155,7 +155,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -173,7 +173,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -182,7 +182,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -328,7 +328,7 @@ "locked": "1.7.36" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ @@ -342,37 +342,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-client": { "project": true @@ -411,13 +411,13 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -434,7 +434,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -452,7 +452,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -461,7 +461,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -470,7 +470,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -479,7 +479,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -488,7 +488,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -497,7 +497,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -506,7 +506,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -515,7 +515,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -524,7 +524,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -533,7 +533,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-java-sdk" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -688,13 +688,13 @@ "locked": "1.7.36" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/client/dependencies.lock b/client/dependencies.lock index 26b7763be..ae92bb8c3 100644 --- a/client/dependencies.lock +++ b/client/dependencies.lock @@ -1,45 +1,45 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.amazonaws:aws-java-sdk-core": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -92,84 +92,84 @@ }, "runtimeClasspath": { "com.amazonaws:aws-java-sdk-core": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -264,40 +264,40 @@ }, "testCompileClasspath": { "com.amazonaws:aws-java-sdk-core": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -366,10 +366,10 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -377,84 +377,84 @@ }, "testRuntimeClasspath": { "com.amazonaws:aws-java-sdk-core": { - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -564,10 +564,10 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/common/dependencies.lock b/common/dependencies.lock index b046f5bb4..3a2957989 100644 --- a/common/dependencies.lock +++ b/common/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "annotationsProcessorCodegen": { @@ -166,37 +166,37 @@ }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -229,10 +229,10 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -243,67 +243,67 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -356,37 +356,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -425,13 +425,13 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -442,67 +442,67 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -556,13 +556,13 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/core/dependencies.lock b/core/dependencies.lock index 1355a2115..e0f506561 100644 --- a/core/dependencies.lock +++ b/core/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -93,10 +93,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -111,77 +111,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -282,37 +282,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -390,13 +390,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -411,77 +411,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -594,13 +594,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" diff --git a/dependencies.gradle b/dependencies.gradle index 7bf42841f..4864b8cdf 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -17,7 +17,7 @@ ext { revActivation = '2.0.0' revAwaitility = '3.1.6' - revAwsSdk = '1.12.535' + revAwsSdk = '1.12.604' revBval = '2.0.6' revCassandra = '3.10.2' revCassandraUnit = '3.11.2.0' @@ -27,9 +27,9 @@ ext { revElasticSearch6 = '6.8.17' revEmbeddedRedis = '0.6' revEurekaClient = '1.10.10' - revFasterXml = '2.15.0' + revFasterXml = '2.15.3' revGroovy = '3.0.19' - revGrpc = '1.57.+' + revGrpc = '1.59.1' revGuava = '32.1.2-jre' revHamcrestAllMatchers = '1.8' revHealth = '1.1.+' diff --git a/dependencies.lock b/dependencies.lock index 678f11f44..574b845ec 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -69,37 +69,37 @@ }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -122,37 +122,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "junit:junit": { "locked": "4.13.2" @@ -179,10 +179,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -190,37 +190,37 @@ }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "junit:junit": { "locked": "4.13.2" @@ -247,10 +247,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" diff --git a/es6-persistence/dependencies.lock b/es6-persistence/dependencies.lock index 5e7eff0c2..e10fabafe 100644 --- a/es6-persistence/dependencies.lock +++ b/es6-persistence/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -78,7 +78,7 @@ "locked": "6.8.17" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -94,7 +94,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -102,7 +102,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -110,7 +110,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -118,7 +118,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -126,7 +126,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -134,7 +134,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -142,7 +142,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -150,7 +150,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -158,7 +158,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -166,7 +166,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -174,7 +174,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -329,37 +329,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -413,10 +413,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -435,7 +435,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -451,7 +451,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -459,7 +459,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -475,7 +475,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -483,7 +483,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -491,7 +491,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -499,7 +499,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -507,7 +507,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -515,7 +515,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -672,10 +672,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" diff --git a/grpc-client/dependencies.lock b/grpc-client/dependencies.lock index 1b9d4a9db..6a271634a 100644 --- a/grpc-client/dependencies.lock +++ b/grpc-client/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -51,13 +51,13 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "org.apache.commons:commons-lang3": { "locked": "3.12.0" @@ -91,7 +91,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -99,7 +99,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -107,7 +107,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -115,7 +115,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -123,7 +123,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -131,7 +131,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -139,7 +139,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -147,7 +147,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -155,7 +155,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -163,7 +163,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -171,7 +171,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -199,19 +199,19 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ @@ -285,37 +285,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -330,13 +330,13 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "junit:junit": { "locked": "4.13.2" @@ -369,10 +369,10 @@ "locked": "1.7.36" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -385,7 +385,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -393,7 +393,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -401,7 +401,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -409,7 +409,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -417,7 +417,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -425,7 +425,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -433,7 +433,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -441,7 +441,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -449,7 +449,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -457,7 +457,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -465,7 +465,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -493,19 +493,19 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ @@ -578,10 +578,10 @@ "locked": "1.7.36" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/grpc-server/dependencies.lock b/grpc-server/dependencies.lock index 95946d261..98ac4cad5 100644 --- a/grpc-server/dependencies.lock +++ b/grpc-server/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -48,13 +48,13 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { - "locked": "1.57.2" + "locked": "1.59.1" }, "org.apache.commons:commons-lang3": { "locked": "3.12.0" @@ -75,7 +75,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -89,7 +89,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -98,7 +98,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -107,7 +107,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -116,7 +116,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -125,7 +125,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -134,7 +134,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -143,7 +143,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -152,7 +152,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -161,7 +161,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -170,7 +170,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -179,7 +179,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -239,22 +239,22 @@ "locked": "2.7" }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ @@ -357,37 +357,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -399,16 +399,16 @@ "project": true }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-testing": { - "locked": "1.57.2" + "locked": "1.59.1" }, "junit:junit": { "locked": "4.13.2" @@ -438,10 +438,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testinfected.hamcrest-matchers:all-matchers": { "locked": "1.8" @@ -458,7 +458,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -476,7 +476,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -485,7 +485,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -494,7 +494,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -503,7 +503,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -512,7 +512,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -521,7 +521,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -530,7 +530,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -539,7 +539,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -548,7 +548,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -608,25 +608,25 @@ "locked": "2.7" }, "io.grpc:grpc-netty": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-testing": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ @@ -727,10 +727,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testinfected.hamcrest-matchers:all-matchers": { "locked": "1.8" diff --git a/grpc/dependencies.lock b/grpc/dependencies.lock index 7aa9f94d7..cf7a62e28 100644 --- a/grpc/dependencies.lock +++ b/grpc/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -45,10 +45,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -78,77 +78,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -166,10 +166,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -231,7 +231,7 @@ }, "protobufToolsLocator_grpc": { "io.grpc:protoc-gen-grpc-java": { - "locked": "1.57.2" + "locked": "1.59.1" } }, "protobufToolsLocator_protoc": { @@ -245,77 +245,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -333,10 +333,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -398,37 +398,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -437,10 +437,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -470,10 +470,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -485,77 +485,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -573,10 +573,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -638,10 +638,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ @@ -657,77 +657,77 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -745,10 +745,10 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "javax.annotation:javax.annotation-api": { "locked": "1.3.2" @@ -810,10 +810,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/http-task/dependencies.lock b/http-task/dependencies.lock index 6076edb48..f2ded231e 100644 --- a/http-task/dependencies.lock +++ b/http-task/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -63,10 +63,10 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -79,7 +79,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -87,7 +87,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -95,7 +95,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -103,7 +103,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -111,7 +111,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -119,7 +119,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -127,7 +127,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -135,7 +135,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -143,7 +143,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -151,7 +151,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -159,7 +159,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -305,37 +305,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -380,13 +380,13 @@ "locked": "5.12.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:mockserver": { "locked": "1.19.1" @@ -402,7 +402,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -410,7 +410,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -418,7 +418,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -426,7 +426,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -434,7 +434,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -442,7 +442,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -450,7 +450,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -458,7 +458,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -466,7 +466,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -474,7 +474,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -482,7 +482,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -636,13 +636,13 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:mockserver": { "locked": "1.19.1" diff --git a/java-sdk/dependencies.lock b/java-sdk/dependencies.lock index 73ec95b16..7f9b9098b 100644 --- a/java-sdk/dependencies.lock +++ b/java-sdk/dependencies.lock @@ -1,7 +1,7 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { @@ -9,37 +9,37 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -89,7 +89,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -97,7 +97,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -105,7 +105,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -113,7 +113,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -121,7 +121,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -129,7 +129,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -137,7 +137,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -145,7 +145,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -153,7 +153,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -161,7 +161,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -169,7 +169,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -177,7 +177,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -315,37 +315,37 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -405,13 +405,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework:spring-web": { - "locked": "5.3.30" + "locked": "5.3.31" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -425,7 +425,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ @@ -433,7 +433,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -441,7 +441,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -449,7 +449,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -457,7 +457,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -465,7 +465,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -473,7 +473,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -481,7 +481,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -489,7 +489,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -497,7 +497,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -505,7 +505,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -513,7 +513,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -656,13 +656,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework:spring-web": { - "locked": "5.3.30" + "locked": "5.3.31" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/json-jq-task/dependencies.lock b/json-jq-task/dependencies.lock index 57744304a..6731c8cf1 100644 --- a/json-jq-task/dependencies.lock +++ b/json-jq-task/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -66,7 +66,7 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -79,7 +79,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -87,7 +87,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -95,7 +95,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -103,7 +103,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -111,7 +111,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -119,7 +119,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -127,7 +127,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -135,7 +135,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -143,7 +143,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -151,7 +151,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -159,7 +159,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -305,37 +305,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "locked": "2.9.3" @@ -374,10 +374,10 @@ "locked": "5.8.2" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -390,7 +390,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -398,7 +398,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -406,7 +406,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -414,7 +414,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -422,7 +422,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -430,7 +430,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -438,7 +438,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -446,7 +446,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -454,7 +454,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -462,7 +462,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -470,7 +470,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -615,10 +615,10 @@ "locked": "15.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/redis-concurrency-limit/dependencies.lock b/redis-concurrency-limit/dependencies.lock index 8f8af5d59..8c61b8f60 100644 --- a/redis-concurrency-limit/dependencies.lock +++ b/redis-concurrency-limit/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -63,10 +63,10 @@ "locked": "2.17.2" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -82,7 +82,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -90,7 +90,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -98,7 +98,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -106,7 +106,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -114,7 +114,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -122,7 +122,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -130,7 +130,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -138,7 +138,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -146,7 +146,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -154,7 +154,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -162,7 +162,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -308,37 +308,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -386,13 +386,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:spock": { "locked": "1.19.1" @@ -414,7 +414,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -422,7 +422,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -430,7 +430,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -438,7 +438,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -446,7 +446,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -454,7 +454,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -462,7 +462,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -470,7 +470,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -478,7 +478,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -486,7 +486,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -494,7 +494,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -645,13 +645,13 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.testcontainers:spock": { "locked": "1.19.1" diff --git a/redis-lock/dependencies.lock b/redis-lock/dependencies.lock index dfb2b1cef..4c9d61e36 100644 --- a/redis-lock/dependencies.lock +++ b/redis-lock/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-core": { "project": true @@ -63,7 +63,7 @@ "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -76,7 +76,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -84,7 +84,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -92,7 +92,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -100,7 +100,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -108,7 +108,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -116,7 +116,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -124,7 +124,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -132,7 +132,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -140,7 +140,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -148,7 +148,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -302,37 +302,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.kstyrc:embedded-redis": { "locked": "0.6" @@ -371,10 +371,10 @@ "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -387,7 +387,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -395,7 +395,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -403,7 +403,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -411,7 +411,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -419,7 +419,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -427,7 +427,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -435,7 +435,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -451,7 +451,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -459,7 +459,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -615,10 +615,10 @@ "locked": "3.13.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/redis-persistence/dependencies.lock b/redis-persistence/dependencies.lock index 545c29079..7e8f781d8 100644 --- a/redis-persistence/dependencies.lock +++ b/redis-persistence/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -69,7 +69,7 @@ "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -85,7 +85,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -93,7 +93,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -101,7 +101,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -109,7 +109,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -117,7 +117,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -125,7 +125,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -133,7 +133,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -141,7 +141,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -149,7 +149,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -157,7 +157,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -165,7 +165,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -320,37 +320,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -392,10 +392,10 @@ "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -411,7 +411,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -419,7 +419,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -427,7 +427,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -435,7 +435,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -443,7 +443,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -451,7 +451,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -459,7 +459,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -467,7 +467,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -475,7 +475,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -483,7 +483,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -491,7 +491,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -642,10 +642,10 @@ "locked": "0.0.17" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/rest/dependencies.lock b/rest/dependencies.lock index 3e0510c7d..00169a86d 100644 --- a/rest/dependencies.lock +++ b/rest/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -66,7 +66,7 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -79,7 +79,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -87,7 +87,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -95,7 +95,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -103,7 +103,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -111,7 +111,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -119,7 +119,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -127,7 +127,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -135,7 +135,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -143,7 +143,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -151,7 +151,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -159,7 +159,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -298,7 +298,7 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ @@ -311,37 +311,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-common": { "project": true @@ -380,13 +380,13 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "locked": "2.0" @@ -399,7 +399,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ @@ -407,7 +407,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ @@ -415,7 +415,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ @@ -423,7 +423,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ @@ -431,7 +431,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ @@ -439,7 +439,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ @@ -447,7 +447,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ @@ -455,7 +455,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ @@ -463,7 +463,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ @@ -471,7 +471,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ @@ -479,7 +479,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -627,13 +627,13 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ diff --git a/server/dependencies.lock b/server/dependencies.lock index b148e4700..e8aa04b88 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.netflix.conductor:conductor-awss3-storage": { "project": true @@ -96,19 +96,19 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -122,13 +122,13 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-awss3-storage" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.amazonaws:aws-java-sdk-sqs": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-awssqs-event-queue" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ @@ -138,11 +138,9 @@ }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", @@ -154,11 +152,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -174,11 +171,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -194,16 +190,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -214,16 +207,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -234,16 +224,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -254,16 +241,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -274,16 +258,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -294,16 +275,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -314,16 +292,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -334,16 +309,14 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -354,7 +327,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -503,26 +476,26 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -569,7 +542,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.0.6" + "locked": "2.0.5" }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ @@ -706,12 +679,6 @@ "org.glassfish.jaxb:jaxb-runtime": { "locked": "2.3.3" }, - "org.openjdk.nashorn:nashorn-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-core" - ], - "locked": "15.4" - }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -731,34 +698,31 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -784,13 +748,13 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-awss3-storage" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.amazonaws:aws-java-sdk-sqs": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-awssqs-event-queue" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ @@ -800,11 +764,9 @@ }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", @@ -816,11 +778,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -836,11 +797,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -856,16 +816,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -876,16 +833,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -896,16 +850,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -916,16 +867,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -936,16 +884,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -956,16 +901,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -976,16 +918,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -996,16 +935,14 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1016,7 +953,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -1165,26 +1102,26 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -1231,7 +1168,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.0.6" + "locked": "2.0.5" }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ @@ -1368,12 +1305,6 @@ "org.glassfish.jaxb:jaxb-runtime": { "locked": "2.3.3" }, - "org.openjdk.nashorn:nashorn-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-core" - ], - "locked": "15.4" - }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -1393,34 +1324,31 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1443,37 +1371,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.24.3" @@ -1518,13 +1446,13 @@ "project": true }, "io.grpc:grpc-protobuf": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-testing": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -1557,22 +1485,22 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" @@ -1586,13 +1514,13 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-awss3-storage" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.amazonaws:aws-java-sdk-sqs": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-awssqs-event-queue" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ @@ -1602,11 +1530,9 @@ }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", @@ -1618,11 +1544,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -1638,11 +1563,10 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -1658,16 +1582,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1678,16 +1599,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1698,16 +1616,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1718,16 +1633,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1738,16 +1650,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1758,16 +1667,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1778,16 +1684,13 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1798,16 +1701,14 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", @@ -1818,7 +1719,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -1967,29 +1868,29 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-testing": { - "locked": "1.57.2" + "locked": "1.59.1" }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.3" @@ -2042,7 +1943,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.0.6" + "locked": "2.0.5" }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ @@ -2182,12 +2083,6 @@ "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, - "org.openjdk.nashorn:nashorn-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-core" - ], - "locked": "15.4" - }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -2207,37 +2102,34 @@ "locked": "1.6.15" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 50979c9bd..0a6f5f251 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -1,42 +1,42 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "2.7.18" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -59,37 +59,37 @@ }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.17.2" @@ -112,37 +112,37 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.0" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "32.1.2-jre" @@ -238,16 +238,16 @@ "locked": "2.3-groovy-3.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "locked": "1.3.4" }, "org.springframework:spring-web": { - "locked": "5.3.30" + "locked": "5.3.31" }, "org.testcontainers:elasticsearch": { "locked": "1.19.1" @@ -264,19 +264,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.amazonaws:aws-java-sdk-s3": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-awss3-storage" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.amazonaws:aws-java-sdk-sqs": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-awssqs-event-queue" ], - "locked": "1.12.535" + "locked": "1.12.604" }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ @@ -286,12 +286,10 @@ }, "com.fasterxml.jackson.core:jackson-annotations": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", @@ -305,11 +303,10 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -328,11 +325,10 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", @@ -351,17 +347,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -374,17 +367,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-smile": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -397,17 +387,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -420,17 +407,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jdk8": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -443,17 +427,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-joda": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -466,17 +447,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -489,17 +467,14 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -512,17 +487,15 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", @@ -535,7 +508,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.15.0" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ @@ -747,7 +720,7 @@ "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ @@ -755,20 +728,20 @@ "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client" ], - "locked": "1.57.2" + "locked": "1.59.1" }, "io.orkes.queues:orkes-conductor-queues": { "firstLevelTransitive": [ @@ -830,7 +803,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.0.6" + "locked": "2.0.5" }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ @@ -988,7 +961,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.3.8" + "locked": "2.3.9" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -999,12 +972,6 @@ "org.junit.vintage:junit-vintage-engine": { "locked": "5.8.2" }, - "org.openjdk.nashorn:nashorn-core": { - "firstLevelTransitive": [ - "com.netflix.conductor:conductor-core" - ], - "locked": "15.4" - }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -1041,35 +1008,35 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-actuator": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-log4j2": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-validation": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.boot:spring-boot-starter-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.7.16" + "locked": "2.7.18" }, "org.springframework.retry:spring-retry": { "firstLevelTransitive": [ @@ -1078,20 +1045,17 @@ "locked": "1.3.4" }, "org.springframework:spring-web": { - "locked": "5.3.30" + "locked": "5.3.31" }, "org.testcontainers:elasticsearch": { "locked": "1.19.1" }, "org.yaml:snakeyaml": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", - "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-es6-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", From 693124e86484ef5c35a8da1863fae3825308af0a Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 09:56:12 -0800 Subject: [PATCH 026/202] dependency locks --- annotations/dependencies.lock | 44 +++--- cassandra-persistence/dependencies.lock | 92 ++++++------ client/dependencies.lock | 92 ++++++------ common/dependencies.lock | 24 +-- core/dependencies.lock | 137 +++++++++++------- .../WorkflowTaskTypeConstraint.java | 9 +- dependencies.gradle | 3 +- dependencies.lock | 44 +++--- es6-persistence/dependencies.lock | 86 +++++------ grpc-client/dependencies.lock | 24 +++ grpc-server/dependencies.lock | 30 +++- http-task/dependencies.lock | 20 +-- java-sdk/dependencies.lock | 86 +++++------ redis-concurrency-limit/dependencies.lock | 110 +++++++------- server/dependencies.lock | 61 ++++++-- test-harness/build.gradle | 4 +- test-harness/dependencies.lock | 129 +++++++++-------- 17 files changed, 567 insertions(+), 428 deletions(-) diff --git a/annotations/dependencies.lock b/annotations/dependencies.lock index 58dd8ff8a..545a15902 100644 --- a/annotations/dependencies.lock +++ b/annotations/dependencies.lock @@ -6,36 +6,36 @@ }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "testCompileClasspath": { @@ -43,22 +43,22 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" @@ -72,22 +72,22 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" diff --git a/cassandra-persistence/dependencies.lock b/cassandra-persistence/dependencies.lock index f7503712c..152c73575 100644 --- a/cassandra-persistence/dependencies.lock +++ b/cassandra-persistence/dependencies.lock @@ -18,19 +18,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -44,33 +44,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -83,7 +83,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -122,19 +122,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,7 +188,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -216,26 +216,26 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" @@ -264,33 +264,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -303,7 +303,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -342,19 +342,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -373,13 +373,16 @@ ], "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -387,7 +390,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -395,7 +398,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -403,7 +406,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -411,13 +414,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ diff --git a/client/dependencies.lock b/client/dependencies.lock index 103511e98..eca08ea7a 100644 --- a/client/dependencies.lock +++ b/client/dependencies.lock @@ -9,10 +9,10 @@ "locked": "1.11.86" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.netflix.conductor:conductor-common": { "project": true @@ -36,19 +36,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -57,7 +57,7 @@ "locked": "23.0.0" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" } }, "runtimeClasspath": { @@ -68,25 +68,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -135,41 +135,41 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" } }, "testCompileClasspath": { @@ -177,10 +177,10 @@ "locked": "1.11.86" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.netflix.conductor:conductor-common": { "project": true @@ -206,29 +206,29 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.powermock:powermock-api-mockito2": { "locked": "2.0.9" @@ -237,7 +237,7 @@ "locked": "2.0.9" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" @@ -260,25 +260,25 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -325,49 +325,49 @@ ], "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.powermock:powermock-api-mockito2": { "locked": "2.0.9" @@ -376,7 +376,7 @@ "locked": "2.0.9" }, "org.slf4j:slf4j-api": { - "locked": "1.7.36" + "locked": "2.0.9" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" diff --git a/common/dependencies.lock b/common/dependencies.lock index b8ea32112..4dfa1dd58 100644 --- a/common/dependencies.lock +++ b/common/dependencies.lock @@ -82,13 +82,13 @@ }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" @@ -129,13 +129,13 @@ }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" @@ -182,13 +182,13 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" @@ -235,13 +235,13 @@ }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { - "locked": "2.15.2" + "locked": "2.15.3" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" diff --git a/core/dependencies.lock b/core/dependencies.lock index cef256a97..b1a1eee7e 100644 --- a/core/dependencies.lock +++ b/core/dependencies.lock @@ -6,16 +6,16 @@ }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" @@ -39,10 +39,10 @@ "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { - "locked": "2.0.0" + "locked": "2.0.1" }, "jakarta.xml.bind:jakarta.xml.bind-api": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "locked": "2.0.5" @@ -51,19 +51,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -75,33 +75,33 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" } }, "runtimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -134,10 +134,10 @@ "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { - "locked": "2.0.0" + "locked": "2.0.1" }, "jakarta.xml.bind:jakarta.xml.bind-api": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -156,35 +156,35 @@ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -192,16 +192,16 @@ }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "locked": "3.21.12" @@ -225,10 +225,10 @@ "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { - "locked": "2.0.0" + "locked": "2.0.1" }, "jakarta.xml.bind:jakarta.xml.bind-api": { - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -239,29 +239,29 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.glassfish.jaxb:jaxb-runtime": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -282,33 +282,33 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" } }, "testRuntimeClasspath": { "com.fasterxml.jackson.core:jackson-annotations": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -341,10 +341,10 @@ "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { - "locked": "2.0.0" + "locked": "2.0.1" }, "jakarta.xml.bind:jakarta.xml.bind-api": { - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -364,11 +364,46 @@ "org.apache.groovy:groovy-all": { "locked": "4.0.9" }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common" + ], + "locked": "2.20.0" + }, "org.glassfish.jaxb:jaxb-runtime": { - "locked": "2.3.3" + "locked": "4.0.1" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -389,7 +424,7 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" } } } \ No newline at end of file diff --git a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java index 493fd76b2..2501849be 100644 --- a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java +++ b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java @@ -22,10 +22,6 @@ import java.util.Optional; import javax.script.ScriptException; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; import org.apache.commons.lang3.StringUtils; @@ -35,6 +31,11 @@ import com.netflix.conductor.core.events.ScriptEvaluator; import com.netflix.conductor.core.utils.DateTimeUtils; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; + import static com.netflix.conductor.core.execution.tasks.Terminate.getTerminationStatusParameter; import static com.netflix.conductor.core.execution.tasks.Terminate.validateInputStatus; import static com.netflix.conductor.core.execution.tasks.Wait.DURATION_INPUT; diff --git a/dependencies.gradle b/dependencies.gradle index 2a9d9f64f..769dd5fcd 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -15,7 +15,7 @@ * Common place to define all the version dependencies */ ext { - revActivation = '3.0.2' + revActivation = '2.0.1' revApacheHttpComponentsClient5 = '5.2.1' revAwaitility = '3.1.6' revAwsSdk = '1.11.86' @@ -56,4 +56,5 @@ ext { revSpock = '2.4-M1-groovy-4.0' revSpotifyCompletableFutures = '0.3.3' revTestContainer = '1.15.3' + revFasterXml = '2.15.3' } diff --git a/dependencies.lock b/dependencies.lock index 752d96aac..fd0cb733b 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -6,19 +6,19 @@ }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "jacocoAgent": { @@ -33,19 +33,19 @@ }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "testCompileClasspath": { @@ -53,22 +53,22 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" @@ -82,22 +82,22 @@ "locked": "4.13.2" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" diff --git a/es6-persistence/dependencies.lock b/es6-persistence/dependencies.lock index 32aa282aa..90c1e4bed 100644 --- a/es6-persistence/dependencies.lock +++ b/es6-persistence/dependencies.lock @@ -21,19 +21,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "locked": "6.8.12" @@ -48,7 +48,7 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" } }, "runtimeClasspath": { @@ -56,33 +56,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -98,7 +98,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -137,19 +137,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -171,7 +171,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -179,7 +179,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -187,7 +187,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -195,7 +195,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -203,7 +203,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "locked": "6.8.12" @@ -241,19 +241,19 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" @@ -268,7 +268,7 @@ "locked": "6.8.12" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" @@ -277,7 +277,7 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "org.testcontainers:elasticsearch": { "locked": "1.15.3" @@ -288,33 +288,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -330,7 +330,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -369,19 +369,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -406,7 +406,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -414,7 +414,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -422,7 +422,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -430,7 +430,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -438,7 +438,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" @@ -453,7 +453,7 @@ "locked": "6.8.12" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -468,7 +468,7 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "org.testcontainers:elasticsearch": { "locked": "1.15.3" diff --git a/grpc-client/dependencies.lock b/grpc-client/dependencies.lock index 9876ffb92..a0dd3f493 100644 --- a/grpc-client/dependencies.lock +++ b/grpc-client/dependencies.lock @@ -26,6 +26,9 @@ "io.grpc:grpc-stub": { "locked": "1.57.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" + }, "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, @@ -108,8 +111,17 @@ "locked": "1.57.2" }, "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], "locked": "2.1.1" }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -188,6 +200,9 @@ "io.grpc:grpc-stub": { "locked": "1.57.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "locked": "2.1.1" + }, "junit:junit": { "locked": "4.13.2" }, @@ -282,8 +297,17 @@ "locked": "1.57.2" }, "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], "locked": "2.1.1" }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, "junit:junit": { "locked": "4.13.2" }, diff --git a/grpc-server/dependencies.lock b/grpc-server/dependencies.lock index 144391fc1..ae4dd2f1c 100644 --- a/grpc-server/dependencies.lock +++ b/grpc-server/dependencies.lock @@ -17,6 +17,9 @@ "io.grpc:grpc-netty": { "locked": "1.57.2" }, + "io.grpc:grpc-protobuf": { + "locked": "1.57.2" + }, "io.grpc:grpc-services": { "locked": "1.57.2" }, @@ -81,7 +84,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.24.0" + "locked": "3.22.3" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -156,17 +159,23 @@ ], "locked": "2.1.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -247,6 +256,9 @@ "io.grpc:grpc-netty": { "locked": "1.57.2" }, + "io.grpc:grpc-protobuf": { + "locked": "1.57.2" + }, "io.grpc:grpc-services": { "locked": "1.57.2" }, @@ -326,7 +338,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.24.0" + "locked": "3.22.3" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -404,17 +416,23 @@ ], "locked": "2.1.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "junit:junit": { "locked": "4.13.2" diff --git a/http-task/dependencies.lock b/http-task/dependencies.lock index 9004cb56d..8fd71e7f0 100644 --- a/http-task/dependencies.lock +++ b/http-task/dependencies.lock @@ -15,7 +15,7 @@ "locked": "1.1.1" }, "org.apache.httpcomponents.client5:httpclient5": { - "locked": "5.2.1" + "locked": "5.2.1" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.20.0" @@ -139,9 +139,6 @@ "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, - "org.apache.httpcomponents.client5:httpclient5": { - "locked": "5.2.1" - }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", @@ -156,6 +153,9 @@ ], "locked": "3.12.0" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", @@ -213,12 +213,12 @@ "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, - "org.apache.httpcomponents.client5:httpclient5": { - "locked": "5.2.1" - }, "junit:junit": { "locked": "4.13.2" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "locked": "2.20.0" }, @@ -359,9 +359,6 @@ "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, - "org.apache.httpcomponents.client5:httpclient5": { - "locked": "5.2.1" - }, "junit:junit": { "locked": "4.13.2" }, @@ -379,6 +376,9 @@ ], "locked": "3.12.0" }, + "org.apache.httpcomponents.client5:httpclient5": { + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", diff --git a/java-sdk/dependencies.lock b/java-sdk/dependencies.lock index 273c00559..0d209a90f 100644 --- a/java-sdk/dependencies.lock +++ b/java-sdk/dependencies.lock @@ -9,7 +9,7 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -27,19 +27,19 @@ "locked": "2.1.1" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" @@ -62,31 +62,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -161,7 +161,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -169,7 +169,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -177,7 +177,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -185,7 +185,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -193,7 +193,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -208,7 +208,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.7.36" + "locked": "2.0.9" } }, "testCompileClasspath": { @@ -216,10 +216,10 @@ "locked": "3.3.0" }, "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -242,29 +242,29 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -299,31 +299,31 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -395,13 +395,16 @@ ], "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -409,7 +412,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -417,7 +420,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -425,7 +428,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -433,10 +436,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common" ], - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -445,7 +445,7 @@ "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "locked": "15.4" @@ -454,7 +454,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "1.7.36" + "locked": "2.0.9" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" diff --git a/redis-concurrency-limit/dependencies.lock b/redis-concurrency-limit/dependencies.lock index 936087215..11a682428 100644 --- a/redis-concurrency-limit/dependencies.lock +++ b/redis-concurrency-limit/dependencies.lock @@ -15,28 +15,28 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.2" + "locked": "3.1.4" }, "redis.clients:jedis": { - "locked": "3.3.0" + "locked": "3.6.0" } }, "runtimeClasspath": { @@ -44,33 +44,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -83,7 +83,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -122,19 +122,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "org.apache.bval:bval-jsr": { "firstLevelTransitive": [ @@ -156,7 +156,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -164,7 +164,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -172,7 +172,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -180,7 +180,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -188,10 +188,16 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" }, "redis.clients:jedis": { - "locked": "3.3.0" + "locked": "3.6.0" } }, "testCompileClasspath": { @@ -210,26 +216,26 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" @@ -244,7 +250,7 @@ "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.2" + "locked": "3.1.4" }, "org.testcontainers:spock": { "locked": "1.15.3" @@ -253,7 +259,7 @@ "locked": "1.15.3" }, "redis.clients:jedis": { - "locked": "3.3.0" + "locked": "3.6.0" } }, "testRuntimeClasspath": { @@ -261,33 +267,33 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.protobuf:protobuf-java": { "firstLevelTransitive": [ @@ -300,7 +306,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -339,19 +345,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, "junit:junit": { "locked": "4.13.2" @@ -370,13 +376,16 @@ ], "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -384,7 +393,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -392,7 +401,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -400,7 +409,7 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -408,13 +417,10 @@ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -435,7 +441,7 @@ "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "2.7.2" + "locked": "3.1.4" }, "org.testcontainers:spock": { "locked": "1.15.3" diff --git a/server/dependencies.lock b/server/dependencies.lock index cb77006ed..ba811c2ce 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -149,7 +149,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.24.0" + "locked": "3.22.3" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -280,7 +280,8 @@ }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-grpc" + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server" ], "locked": "1.57.2" }, @@ -312,17 +313,23 @@ ], "locked": "2.1.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -357,6 +364,12 @@ ], "locked": "3.12.0" }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", @@ -596,7 +609,7 @@ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-grpc" ], - "locked": "3.24.0" + "locked": "3.22.3" }, "com.jayway.jsonpath:json-path": { "firstLevelTransitive": [ @@ -727,7 +740,8 @@ }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-grpc" + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server" ], "locked": "1.57.2" }, @@ -759,17 +773,23 @@ ], "locked": "2.1.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -804,6 +824,12 @@ ], "locked": "3.12.0" }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", @@ -1278,7 +1304,8 @@ }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-grpc" + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server" ], "locked": "1.57.2" }, @@ -1313,17 +1340,23 @@ ], "locked": "2.1.2" }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ @@ -1361,6 +1394,12 @@ ], "locked": "3.12.0" }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", diff --git a/test-harness/build.gradle b/test-harness/build.gradle index 8c49d91fd..ea410db62 100644 --- a/test-harness/build.gradle +++ b/test-harness/build.gradle @@ -32,8 +32,8 @@ dependencies { testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" - testImplementation "org.elasticsearch.client:elasticsearch-rest-client" - testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" + testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" testImplementation('junit:junit:4.13.2') diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 95bb8411d..4266b0e15 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -6,44 +6,44 @@ }, "compileClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "runtimeClasspath": { "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" + "locked": "2.20.0" } }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.3" }, "com.google.guava:guava": { "locked": "30.0-jre" @@ -99,35 +99,35 @@ "org.apache.commons:commons-lang3": { "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.glassfish.jersey.core:jersey-common": { "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" @@ -142,7 +142,7 @@ "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.3" + "locked": "2.0.3" }, "org.springframework:spring-web": { "locked": "6.0.12" @@ -183,46 +183,46 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.3" }, "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.module:jackson-module-afterburner": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.github.ben-manes.caffeine:caffeine": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", "com.netflix.conductor:conductor-json-jq-task" ], - "locked": "2.9.3" + "locked": "3.1.8" }, "com.google.guava:guava": { "firstLevelTransitive": [ @@ -245,7 +245,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.7.0" + "locked": "2.8.0" }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ @@ -427,27 +427,28 @@ "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-protobuf": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", - "com.netflix.conductor:conductor-grpc-client" + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-services": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc-server" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.grpc:grpc-stub": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client" ], - "locked": "1.57.1" + "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { "firstLevelTransitive": [ @@ -460,25 +461,32 @@ "com.netflix.conductor:conductor-awssqs-event-queue", "com.netflix.conductor:conductor-core" ], - "locked": "1.3.8" + "locked": "1.2.2" }, "jakarta.activation:jakarta.activation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "1.2.2" + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" }, "jakarta.xml.bind:jakarta.xml.bind-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" ], - "locked": "2.3.3" + "locked": "4.0.1" }, - "jakarta.annotation:jakarta.annotation-api": { + "javax.annotation:javax.annotation-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-grpc" ], - "locked": "2.1.1" + "locked": "1.3.2" }, "javax.ws.rs:javax.ws.rs-api": { "firstLevelTransitive": [ @@ -524,6 +532,15 @@ ], "locked": "3.12.0" }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-annotations", @@ -545,7 +562,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ @@ -568,7 +585,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ @@ -591,7 +608,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ @@ -614,7 +631,7 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.21.0" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ @@ -637,22 +654,19 @@ "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "2.21.0" - }, - "org.apache.groovy:groovy-all": { - "locked": "4.0.9" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { "firstLevelTransitive": [ @@ -664,7 +678,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "2.3.6" + "locked": "4.0.3" }, "org.glassfish.jersey.core:jersey-common": { "firstLevelTransitive": [ @@ -673,7 +687,7 @@ "locked": "2.22.2" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -698,7 +712,7 @@ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-grpc-client" ], - "locked": "1.7.36" + "locked": "2.0.9" }, "org.spockframework:spock-core": { "locked": "2.4-M1-groovy-4.0" @@ -751,7 +765,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "1.3.3" + "locked": "2.0.3" }, "org.springframework:spring-web": { "locked": "6.0.12" @@ -762,7 +776,8 @@ "redis.clients:jedis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-concurrency-limit", - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" ], "locked": "3.3.0" } From c7652a98685be1ac7ff57e93e1632ab1a96a672f Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 10:02:04 -0800 Subject: [PATCH 027/202] dependencies --- dependencies.gradle | 2 +- server/dependencies.lock | 10 +++++----- test-harness/dependencies.lock | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 769dd5fcd..1fbe9dad7 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -44,7 +44,7 @@ ext { revJsr311Api = '1.1.1' revMockServerClient = '5.12.0' revOpenapi = '1.6.+' - revOrkesQueues = '1.0.3' + revOrkesQueues = '1.0.7' revPowerMock = '2.0.9' revProtoBuf = '3.21.12' revProtogenAnnotations = '1.0.0' diff --git a/server/dependencies.lock b/server/dependencies.lock index ba811c2ce..551cb5f20 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -42,7 +42,7 @@ "project": true }, "io.orkes.queues:orkes-conductor-queues": { - "locked": "1.0.3" + "locked": "1.0.7" }, "org.apache.logging.log4j:log4j-api": { "locked": "2.20.0" @@ -298,7 +298,7 @@ "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { - "locked": "1.0.3" + "locked": "1.0.7" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ @@ -758,7 +758,7 @@ "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { - "locked": "1.0.3" + "locked": "1.0.7" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ @@ -1057,7 +1057,7 @@ "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { - "locked": "1.0.3" + "locked": "1.0.7" }, "junit:junit": { "locked": "4.13.2" @@ -1325,7 +1325,7 @@ "locked": "1.57.2" }, "io.orkes.queues:orkes-conductor-queues": { - "locked": "1.0.3" + "locked": "1.0.7" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 4266b0e15..3473f8f56 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -454,7 +454,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], - "locked": "1.0.3" + "locked": "1.0.7" }, "io.reactivex:rxjava": { "firstLevelTransitive": [ From 9e2c85eaca07b365945b8acbb7d19201c271fb28 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 10:16:04 -0800 Subject: [PATCH 028/202] springdocs --- common/build.gradle | 2 +- dependencies.gradle | 2 +- rest/build.gradle | 2 +- server/build.gradle | 3 ++- server/src/main/resources/application.properties | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 5f20d89b8..ef64ff263 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.springframework.boot:spring-boot-starter' compileOnly 'org.springframework.boot:spring-boot-starter-validation' - compileOnly "org.springdoc:springdoc-openapi-ui:${revOpenapi}" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${revSpringDoc}" implementation "org.apache.commons:commons-lang3" diff --git a/dependencies.gradle b/dependencies.gradle index 1fbe9dad7..41c0f0317 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -43,7 +43,7 @@ ext { revJq = '0.0.13' revJsr311Api = '1.1.1' revMockServerClient = '5.12.0' - revOpenapi = '1.6.+' + revSpringDoc = '2.1.0' revOrkesQueues = '1.0.7' revPowerMock = '2.0.9' revProtoBuf = '3.21.12' diff --git a/rest/build.gradle b/rest/build.gradle index 97d66d816..dc19c73dd 100644 --- a/rest/build.gradle +++ b/rest/build.gradle @@ -7,5 +7,5 @@ dependencies { implementation "com.netflix.runtime:health-api:${revHealth}" - implementation "org.springdoc:springdoc-openapi-ui:${revOpenapi}" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${revSpringDoc}" } diff --git a/server/build.gradle b/server/build.gradle index 7b87576e9..38ebba808 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -42,7 +42,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation "io.orkes.queues:orkes-conductor-queues:${revOrkesQueues}" - implementation "org.springdoc:springdoc-openapi-ui:${revOpenapi}" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${revSpringDoc}" + runtimeOnly "org.glassfish.jaxb:jaxb-runtime:${revJAXB}" diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 09b8f22b4..9889a2022 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -15,7 +15,7 @@ spring.application.name=conductor springdoc.api-docs.path=/api-docs loadSample=true -conductor.db.type=memory +conductor.db.type=redis_standalone conductor.queue.type=redis_standalone conductor.indexing.enabled=false From 67f699ca70abd091e2549ad21ae8beb40ef6adac Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 10:17:28 -0800 Subject: [PATCH 029/202] Update ci.yml --- .github/workflows/ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9528884b4..f4cd98635 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,12 +42,8 @@ jobs: if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | echo "Running build for commit ${{ github.sha }}" - ./gradlew build snapshot --scan + ./gradlew build env: - NETFLIX_OSS_SIGNING_KEY: ${{ secrets.ORG_SIGNING_KEY }} - NETFLIX_OSS_SIGNING_PASSWORD: ${{ secrets.ORG_SIGNING_PASSWORD }} - NETFLIX_OSS_REPO_USERNAME: ${{ secrets.ORG_NETFLIXOSS_USERNAME }} - NETFLIX_OSS_REPO_PASSWORD: ${{ secrets.ORG_NETFLIXOSS_PASSWORD }} - name: Publish Test Report uses: mikepenz/action-junit-report@v3 if: always() From dbbe6b5e7c855e1cc9718a827b420b3ca4d1b747 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 10:31:13 -0800 Subject: [PATCH 030/202] Update ci.yml --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4cd98635..a25c97dd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,6 @@ jobs: run: | echo "Running build for commit ${{ github.sha }}" ./gradlew build - env: - name: Publish Test Report uses: mikepenz/action-junit-report@v3 if: always() From ebd0fa2d152b4fcaac748127cf5856d5fb95ff2f Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 10:50:06 -0800 Subject: [PATCH 031/202] dependencies --- redis-concurrency-limit/build.gradle | 2 +- redis-concurrency-limit/dependencies.lock | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/redis-concurrency-limit/build.gradle b/redis-concurrency-limit/build.gradle index b0061578d..20c508809 100644 --- a/redis-concurrency-limit/build.gradle +++ b/redis-concurrency-limit/build.gradle @@ -17,5 +17,5 @@ dependencies { testImplementation "org.testcontainers:spock:${revTestContainer}" testImplementation "org.testcontainers:testcontainers:${revTestContainer}" testImplementation "com.google.protobuf:protobuf-java:${revProtoBuf}" - testImplementation 'org.springframework.data:spring-data-redis' + testImplementation 'org.springframework.data:spring-data-redis:2.7.16' } diff --git a/redis-concurrency-limit/dependencies.lock b/redis-concurrency-limit/dependencies.lock index 11a682428..3a3cb7f21 100644 --- a/redis-concurrency-limit/dependencies.lock +++ b/redis-concurrency-limit/dependencies.lock @@ -33,7 +33,7 @@ "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "3.1.4" + "locked": "2.7.16" }, "redis.clients:jedis": { "locked": "3.6.0" @@ -196,6 +196,12 @@ ], "locked": "15.4" }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + }, "redis.clients:jedis": { "locked": "3.6.0" } @@ -250,7 +256,7 @@ "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "3.1.4" + "locked": "2.7.16" }, "org.testcontainers:spock": { "locked": "1.15.3" @@ -434,6 +440,12 @@ "org.spockframework:spock-spring": { "locked": "2.4-M1-groovy-4.0" }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + }, "org.springframework.boot:spring-boot-starter-log4j2": { "locked": "3.1.4" }, @@ -441,7 +453,7 @@ "locked": "3.1.4" }, "org.springframework.data:spring-data-redis": { - "locked": "3.1.4" + "locked": "2.7.16" }, "org.testcontainers:spock": { "locked": "1.15.3" From da92c670d3a0c1ee10b41ad2dc3352c217da0156 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 12:39:10 -0800 Subject: [PATCH 032/202] status listener --- dependencies.gradle | 2 + settings.gradle | 5 + test-harness/dependencies.lock | 5 +- test-util/build.gradle | 55 + test-util/dependencies.lock | 1452 +++++++++++++++++ .../AbstractResiliencySpecification.groovy | 74 + .../test/base/AbstractSpecification.groovy | 61 + .../test/util/WorkflowTestUtil.groovy | 332 ++++ .../netflix/conductor/ConductorTestApp.java | 31 + .../config/TestObjectMapperConfiguration.java | 27 + .../integration/AbstractEndToEndTest.java | 284 ++++ .../grpc/AbstractGrpcEndToEndTest.java | 252 +++ .../application-integrationtest.properties | 41 + workflow-event-listener/README.md | 49 + workflow-event-listener/build.gradle | 25 + workflow-event-listener/dependencies.lock | 1170 +++++++++++++ ...rchivingWithTTLWorkflowStatusListener.java | 134 ++ ...rchivingWorkflowListenerConfiguration.java | 36 + .../ArchivingWorkflowListenerProperties.java | 66 + .../ArchivingWorkflowStatusListener.java | 51 + .../ConductorQueueStatusPublisher.java | 84 + ...ctorQueueStatusPublisherConfiguration.java | 38 + ...nductorQueueStatusPublisherProperties.java | 48 + ...itional-spring-configuration-metadata.json | 136 ++ .../ArchivingWorkflowStatusListenerTest.java | 62 + ...orkflowStatusPublisherIntegrationTest.java | 214 +++ .../application-integrationtest.properties | 55 + 27 files changed, 4787 insertions(+), 2 deletions(-) create mode 100644 test-util/build.gradle create mode 100644 test-util/dependencies.lock create mode 100644 test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy create mode 100644 test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy create mode 100644 test-util/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy create mode 100644 test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java create mode 100644 test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java create mode 100644 test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java create mode 100644 test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java create mode 100644 test-util/src/test/resources/application-integrationtest.properties create mode 100644 workflow-event-listener/README.md create mode 100644 workflow-event-listener/build.gradle create mode 100644 workflow-event-listener/dependencies.lock create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java create mode 100644 workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java create mode 100644 workflow-event-listener/src/main/resources/META-INF/additional-spring-configuration-metadata.json create mode 100644 workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java create mode 100644 workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java create mode 100644 workflow-event-listener/src/test/resources/application-integrationtest.properties diff --git a/dependencies.gradle b/dependencies.gradle index 41c0f0317..7e3d65dce 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -57,4 +57,6 @@ ext { revSpotifyCompletableFutures = '0.3.3' revTestContainer = '1.15.3' revFasterXml = '2.15.3' + revAmqpClient = '5.13.0' + } diff --git a/settings.gradle b/settings.gradle index 5394bb5ad..a623e8486 100644 --- a/settings.gradle +++ b/settings.gradle @@ -60,6 +60,11 @@ include 'grpc-client' include 'java-sdk' +// community modules +include 'workflow-event-listener' +include 'test-util' + include 'test-harness' + rootProject.children.each {it.name="conductor-${it.name}"} diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 3473f8f56..04d0076d2 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -720,12 +720,13 @@ "org.spockframework:spock-spring": { "locked": "2.4-M1-groovy-4.0" }, - "org.springdoc:springdoc-openapi-ui": { + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], - "locked": "1.6.15" + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "firstLevelTransitive": [ diff --git a/test-util/build.gradle b/test-util/build.gradle new file mode 100644 index 000000000..b9fe3754b --- /dev/null +++ b/test-util/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'groovy' +} +dependencies { + + + implementation project(':conductor-common') + implementation project(':conductor-core') + implementation project(':conductor-server') + implementation project(':conductor-client') + implementation project(':conductor-rest') + implementation project(':conductor-grpc-server') + implementation project(':conductor-grpc-client') + implementation project(':conductor-redis-persistence') + + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + implementation "org.apache.commons:commons-lang3" + implementation 'org.springframework.boot:spring-boot-starter-validation' + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + + implementation "org.apache.commons:commons-lang3" + + implementation "com.google.protobuf:protobuf-java:${revProtoBuf}" + implementation "com.google.guava:guava:${revGuava}" + testImplementation "org.springframework:spring-web" + + implementation "redis.clients:jedis:${revJedis}" + implementation "com.netflix.dyno-queues:dyno-queues-redis:${revDynoQueues}" + + + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" + testImplementation "org.spockframework:spock-core:${revSpock}" + testImplementation "org.spockframework:spock-spring:${revSpock}" + + implementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" + implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" + + implementation "org.testcontainers:elasticsearch:${revTestContainer}" + implementation "org.testcontainers:mysql:${revTestContainer}" + implementation "org.testcontainers:postgresql:${revTestContainer}" + implementation(group: 'com.rabbitmq', name: 'amqp-client'){ version{require "${revAmqpClient}"}} + + //In memory + implementation "org.rarefiedredis.redis:redis-java:${revRarefiedRedis}" +} + +test { + testLogging { + exceptionFormat = 'full' + } +} diff --git a/test-util/dependencies.lock b/test-util/dependencies.lock new file mode 100644 index 000000000..c2a6b65a5 --- /dev/null +++ b/test-util/dependencies.lock @@ -0,0 +1,1452 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "3.1.4" + } + }, + "compileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "locked": "3.21.12" + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "locked": "2.0.20" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "6.8.23" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "locked": "3.1.4" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "locked": "3.3.0" + } + }, + "runtimeClasspath": { + "com.amazonaws:aws-java-sdk-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.eureka:eureka-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.10.10" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.sun.jersey:jersey-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.19.4" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:javax.ws.rs-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.1.1" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.glassfish.jersey.core:jersey-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "3.1.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.slf4j:slf4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.0.9" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + }, + "testCompileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "locked": "3.21.12" + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "locked": "2.0.20" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "6.8.23" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "locked": "3.1.4" + }, + "org.springframework:spring-web": { + "locked": "6.0.12" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "locked": "3.3.0" + } + }, + "testRuntimeClasspath": { + "com.amazonaws:aws-java-sdk-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.eureka:eureka-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.10.10" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.sun.jersey:jersey-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.19.4" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:javax.ws.rs-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.1.1" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.glassfish.jersey.core:jersey-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "3.1.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.slf4j:slf4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.0.9" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "org.springframework:spring-web": { + "locked": "6.0.12" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + } +} \ No newline at end of file diff --git a/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy b/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy new file mode 100644 index 000000000..d3270c447 --- /dev/null +++ b/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Netflix, Inc. + *

+ * 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 com.netflix.conductor.test.base + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import org.springframework.test.context.TestPropertySource + +import com.netflix.conductor.dao.QueueDAO +import com.netflix.conductor.redis.dao.DynoQueueDAO +import com.netflix.conductor.redis.jedis.JedisMock +import com.netflix.dyno.connectionpool.Host +import com.netflix.dyno.queues.ShardSupplier +import com.netflix.dyno.queues.redis.RedisQueues + +import redis.clients.jedis.commands.JedisCommands +import spock.mock.DetachedMockFactory + +@TestPropertySource(properties = [ + "conductor.system-task-workers.enabled=false", + "conductor.workflow-repair-service.enabled=true", + "conductor.workflow-reconciler.enabled=false", + "conductor.integ-test.queue-spy.enabled=true" +]) +abstract class AbstractResiliencySpecification extends AbstractSpecification { + + @Configuration + static class TestQueueConfiguration { + + @Primary + @Bean + @ConditionalOnProperty(name = "conductor.integ-test.queue-spy.enabled", havingValue = "true") + QueueDAO SpyQueueDAO() { + DetachedMockFactory detachedMockFactory = new DetachedMockFactory() + JedisCommands jedisMock = new JedisMock() + ShardSupplier shardSupplier = new ShardSupplier() { + @Override + Set getQueueShards() { + return new HashSet<>(Collections.singletonList("a")) + } + + @Override + String getCurrentShard() { + return "a" + } + + @Override + String getShardForHost(Host host) { + return "a" + } + } + RedisQueues redisQueues = new RedisQueues(jedisMock, jedisMock, "mockedQueues", shardSupplier, 60000, 120000) + DynoQueueDAO dynoQueueDAO = new DynoQueueDAO(redisQueues) + + return detachedMockFactory.Spy(dynoQueueDAO) + } + } + + @Autowired + QueueDAO queueDAO +} diff --git a/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy b/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy new file mode 100644 index 000000000..4eeea0fa2 --- /dev/null +++ b/test-util/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy @@ -0,0 +1,61 @@ +/* + * Copyright 2021 Netflix, Inc. + *

+ * 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 com.netflix.conductor.test.base + +import com.netflix.conductor.service.WorkflowService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.TestPropertySource + +import com.netflix.conductor.core.execution.AsyncSystemTaskExecutor +import com.netflix.conductor.core.execution.WorkflowExecutor +import com.netflix.conductor.core.reconciliation.WorkflowSweeper +import com.netflix.conductor.service.ExecutionService +import com.netflix.conductor.service.MetadataService +import com.netflix.conductor.test.util.WorkflowTestUtil + +import spock.lang.Specification + +@SpringBootTest +@TestPropertySource(locations = "classpath:application-integrationtest.properties") +abstract class AbstractSpecification extends Specification { + + @Autowired + ExecutionService workflowExecutionService + + @Autowired + MetadataService metadataService + + @Autowired + WorkflowExecutor workflowExecutor + + @Autowired + WorkflowService workflowService + + @Autowired + WorkflowTestUtil workflowTestUtil + + @Autowired + WorkflowSweeper workflowSweeper + + @Autowired + AsyncSystemTaskExecutor asyncSystemTaskExecutor + + def cleanup() { + workflowTestUtil.clearWorkflows() + } + + void sweep(String workflowId) { + workflowSweeper.sweep(workflowId) + } +} diff --git a/test-util/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy b/test-util/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy new file mode 100644 index 000000000..d12cebf8c --- /dev/null +++ b/test-util/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy @@ -0,0 +1,332 @@ +/* + * Copyright 2022 Netflix, Inc. + *

+ * 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 com.netflix.conductor.test.util + +import jakarta.annotation.PostConstruct +import org.apache.commons.lang3.StringUtils +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +import com.netflix.conductor.common.metadata.tasks.Task +import com.netflix.conductor.common.metadata.tasks.TaskDef +import com.netflix.conductor.common.metadata.tasks.TaskResult +import com.netflix.conductor.common.metadata.workflow.WorkflowDef +import com.netflix.conductor.core.WorkflowContext +import com.netflix.conductor.core.exception.NotFoundException +import com.netflix.conductor.core.execution.WorkflowExecutor +import com.netflix.conductor.dao.QueueDAO +import com.netflix.conductor.model.WorkflowModel +import com.netflix.conductor.service.ExecutionService +import com.netflix.conductor.service.MetadataService + +import com.fasterxml.jackson.databind.ObjectMapper + +/** + * This is a helper class used to initialize task definitions required by the tests when loaded up. + * The task definitions that are loaded up in {@link WorkflowTestUtil#taskDefinitions()} method as part of the post construct of the bean. + * This class is intended to be used in the Spock integration tests and provides helper methods to: + *

    + *
  • Terminate all the running Workflows
  • + *
  • Get the persisted task definition based on the taskName
  • + *
  • pollAndFailTask
  • + *
  • pollAndCompleteTask
  • + *
  • verifyPolledAndAcknowledgedTask
  • + *
+ * + * Usage: Autowire this class in any Spock based specification: + * + * {@literal @}Autowired + * WorkflowTestUtil workflowTestUtil + * + */ +@Component +class WorkflowTestUtil { + + private final MetadataService metadataService + private final ExecutionService workflowExecutionService + private final WorkflowExecutor workflowExecutor + private final QueueDAO queueDAO + private final ObjectMapper objectMapper + private static final int RETRY_COUNT = 1 + private static final String TEMP_FILE_PATH = "/input.json" + private static final String DEFAULT_EMAIL_ADDRESS = "test@harness.com" + + @Autowired + WorkflowTestUtil(MetadataService metadataService, ExecutionService workflowExecutionService, + WorkflowExecutor workflowExecutor, QueueDAO queueDAO, ObjectMapper objectMapper) { + this.metadataService = metadataService + this.workflowExecutionService = workflowExecutionService + this.workflowExecutor = workflowExecutor + this.queueDAO = queueDAO + this.objectMapper = objectMapper + } + + /** + * This function registers all the taskDefinitions required to enable spock based integration testing + */ + @PostConstruct + void taskDefinitions() { + WorkflowContext.set(new WorkflowContext("integration_app")) + + (0..20).collect { "integration_task_$it" } + .findAll { !getPersistedTaskDefinition(it).isPresent() } + .collect { new TaskDef(it, it, DEFAULT_EMAIL_ADDRESS, 1, 120, 120) } + .forEach { metadataService.registerTaskDef([it]) } + + (0..4).collect { "integration_task_0_RT_$it" } + .findAll { !getPersistedTaskDefinition(it).isPresent() } + .collect { new TaskDef(it, it, DEFAULT_EMAIL_ADDRESS, 0, 120, 120) } + .forEach { metadataService.registerTaskDef([it]) } + + metadataService.registerTaskDef([new TaskDef('short_time_out', 'short_time_out', DEFAULT_EMAIL_ADDRESS, 1, 5, 5)]) + + //This taskWithResponseTimeOut is required by the integration test which exercises the response time out scenarios + TaskDef taskWithResponseTimeOut = new TaskDef() + taskWithResponseTimeOut.name = "task_rt" + taskWithResponseTimeOut.timeoutSeconds = 120 + taskWithResponseTimeOut.retryCount = RETRY_COUNT + taskWithResponseTimeOut.retryDelaySeconds = 0 + taskWithResponseTimeOut.responseTimeoutSeconds = 10 + taskWithResponseTimeOut.ownerEmail = DEFAULT_EMAIL_ADDRESS + + TaskDef optionalTask = new TaskDef() + optionalTask.setName("task_optional") + optionalTask.setTimeoutSeconds(5) + optionalTask.setRetryCount(1) + optionalTask.setTimeoutPolicy(TaskDef.TimeoutPolicy.RETRY) + optionalTask.setRetryDelaySeconds(0) + optionalTask.setResponseTimeoutSeconds(5) + optionalTask.setOwnerEmail(DEFAULT_EMAIL_ADDRESS) + + TaskDef simpleSubWorkflowTask = new TaskDef() + simpleSubWorkflowTask.setName('simple_task_in_sub_wf') + simpleSubWorkflowTask.setRetryCount(0) + simpleSubWorkflowTask.setOwnerEmail(DEFAULT_EMAIL_ADDRESS) + + TaskDef subWorkflowTask = new TaskDef() + subWorkflowTask.setName('sub_workflow_task') + subWorkflowTask.setRetryCount(1) + subWorkflowTask.setResponseTimeoutSeconds(5) + subWorkflowTask.setRetryDelaySeconds(0) + subWorkflowTask.setOwnerEmail(DEFAULT_EMAIL_ADDRESS) + + TaskDef waitTimeOutTask = new TaskDef() + waitTimeOutTask.name = 'waitTimeout' + waitTimeOutTask.timeoutSeconds = 2 + waitTimeOutTask.responseTimeoutSeconds = 2 + waitTimeOutTask.retryCount = 1 + waitTimeOutTask.timeoutPolicy = TaskDef.TimeoutPolicy.RETRY + waitTimeOutTask.retryDelaySeconds = 10 + waitTimeOutTask.ownerEmail = DEFAULT_EMAIL_ADDRESS + + TaskDef userTask = new TaskDef() + userTask.setName("user_task") + userTask.setTimeoutSeconds(20) + userTask.setResponseTimeoutSeconds(20) + userTask.setRetryCount(1) + userTask.setTimeoutPolicy(TaskDef.TimeoutPolicy.RETRY) + userTask.setRetryDelaySeconds(10) + userTask.setOwnerEmail(DEFAULT_EMAIL_ADDRESS) + + TaskDef concurrentExecutionLimitedTask = new TaskDef() + concurrentExecutionLimitedTask.name = "test_task_with_concurrency_limit" + concurrentExecutionLimitedTask.concurrentExecLimit = 1 + concurrentExecutionLimitedTask.ownerEmail = DEFAULT_EMAIL_ADDRESS + + TaskDef rateLimitedTask = new TaskDef() + rateLimitedTask.name = 'test_task_with_rateLimits' + rateLimitedTask.rateLimitFrequencyInSeconds = 10 + rateLimitedTask.rateLimitPerFrequency = 1 + rateLimitedTask.ownerEmail = DEFAULT_EMAIL_ADDRESS + + TaskDef rateLimitedSimpleTask = new TaskDef() + rateLimitedSimpleTask.name = 'test_simple_task_with_rateLimits' + rateLimitedSimpleTask.rateLimitFrequencyInSeconds = 10 + rateLimitedSimpleTask.rateLimitPerFrequency = 1 + rateLimitedSimpleTask.ownerEmail = DEFAULT_EMAIL_ADDRESS + + TaskDef eventTaskX = new TaskDef() + eventTaskX.name = 'eventX' + eventTaskX.timeoutSeconds = 1 + eventTaskX.responseTimeoutSeconds = 1 + eventTaskX.ownerEmail = DEFAULT_EMAIL_ADDRESS + + metadataService.registerTaskDef( + [taskWithResponseTimeOut, optionalTask, simpleSubWorkflowTask, + subWorkflowTask, waitTimeOutTask, userTask, eventTaskX, + rateLimitedTask, rateLimitedSimpleTask, concurrentExecutionLimitedTask] + ) + } + + /** + * This is an helper method that enables each test feature to run from a clean state + * This method is intended to be used in the cleanup() or cleanupSpec() method of any spock specification. + * By invoking this method all the running workflows are terminated. + * @throws Exception When unable to terminate any running workflow + */ + void clearWorkflows() throws Exception { + List workflowsWithVersion = metadataService.getWorkflowDefs() + .collect { workflowDef -> workflowDef.getName() + ":" + workflowDef.getVersion() } + for (String workflowWithVersion : workflowsWithVersion) { + String workflowName = StringUtils.substringBefore(workflowWithVersion, ":") + int version = Integer.parseInt(StringUtils.substringAfter(workflowWithVersion, ":")) + List running = workflowExecutionService.getRunningWorkflows(workflowName, version) + for (String workflowId : running) { + WorkflowModel workflow = workflowExecutor.getWorkflow(workflowId, false) + if (!workflow.getStatus().isTerminal()) { + workflowExecutor.terminateWorkflow(workflowId, "cleanup") + } + } + } + + queueDAO.queuesDetail().keySet() + .forEach { queueDAO.flush(it) } + + new FileOutputStream(this.getClass().getResource(TEMP_FILE_PATH).getPath()).close() + } + + /** + * A helper method to retrieve a task definition that is persisted + * @param taskDefName The name of the task for which the task definition is requested + * @return an Optional of the TaskDefinition + */ + Optional getPersistedTaskDefinition(String taskDefName) { + try { + return Optional.of(metadataService.getTaskDef(taskDefName)) + } catch (Exception applicationException) { + return Optional.empty() + } + } + + /** + * A helper methods that registers workflows based on the paths of the json file representing a workflow definition + * @param workflowJsonPaths a comma separated var ags of the paths of the workflow definitions + */ + void registerWorkflows(String... workflowJsonPaths) { + workflowJsonPaths.collect { readFile(it) } + .forEach { metadataService.updateWorkflowDef(it) } + } + + WorkflowDef readFile(String path) { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path) + return objectMapper.readValue(inputStream, WorkflowDef.class) + } + + /** + * A helper method intended to be used in the when: block of the spock test feature + * This method is intended to be used to poll and update the task status as failed + * It also provides a delay to return if needed after the task has been updated to failed + * @param taskName name of the task that needs to be polled and failed + * @param workerId name of the worker id using which a task is polled + * @param failureReason the reason to fail the task that will added to the task update + * @param outputParams An optional output parameters if available will be added to the task before updating to failed + * @param waitAtEndSeconds an optional delay before the method returns, if the value is 0 skips the delay + * @return A Tuple of taskResult and acknowledgement of the poll + */ + Tuple pollAndFailTask(String taskName, String workerId, String failureReason, Map outputParams = null, int waitAtEndSeconds = 0) { + def polledIntegrationTask = workflowExecutionService.poll(taskName, workerId) + def ackPolledIntegrationTask = workflowExecutionService.ackTaskReceived(polledIntegrationTask.taskId) + def taskResult = new TaskResult(polledIntegrationTask) + taskResult.status = TaskResult.Status.FAILED + taskResult.reasonForIncompletion = failureReason + if (outputParams) { + outputParams.forEach { k, v -> + taskResult.outputData[k] = v + } + } + workflowExecutionService.updateTask(taskResult) + return waitAtEndSecondsAndReturn(waitAtEndSeconds, polledIntegrationTask, ackPolledIntegrationTask) + } + + /** + * A helper method to introduce delay and convert the polledIntegrationTask and ackPolledIntegrationTask + * into a tuple. This method is intended to be used by pollAndFailTask and pollAndCompleteTask + * @param waitAtEndSeconds The total seconds of delay before the method returns + * @param ackedTaskResult the task result created after ack + * @param ackPolledIntegrationTask a acknowledgement of a poll + * @return A Tuple of polledTask and acknowledgement of the poll + */ + static Tuple waitAtEndSecondsAndReturn(int waitAtEndSeconds, Task polledIntegrationTask, boolean ackPolledIntegrationTask) { + if (waitAtEndSeconds > 0) { + Thread.sleep(waitAtEndSeconds * 1000) + } + return new Tuple(polledIntegrationTask, ackPolledIntegrationTask) + } + + /** + * A helper method intended to be used in the when: block of the spock test feature + * This method is intended to be used to poll and update the task status as completed + * It also provides a delay to return if needed after the task has been updated to completed + * @param taskName name of the task that needs to be polled and completed + * @param workerId name of the worker id using which a task is polled + * @param outputParams An optional output parameters if available will be added to the task before updating to completed + * @param waitAtEndSeconds waitAtEndSeconds an optional delay before the method returns, if the value is 0 skips the delay + * @return A Tuple of polledTask and acknowledgement of the poll + */ + Tuple pollAndCompleteTask(String taskName, String workerId, Map outputParams = null, int waitAtEndSeconds = 0) { + def polledIntegrationTask = workflowExecutionService.poll(taskName, workerId) + if (polledIntegrationTask == null) { + return new Tuple(null, null) + } + def ackPolledIntegrationTask = workflowExecutionService.ackTaskReceived(polledIntegrationTask.taskId) + def taskResult = new TaskResult(polledIntegrationTask) + taskResult.status = TaskResult.Status.COMPLETED + if (outputParams) { + outputParams.forEach { k, v -> + taskResult.outputData[k] = v + } + } + workflowExecutionService.updateTask(taskResult) + return waitAtEndSecondsAndReturn(waitAtEndSeconds, polledIntegrationTask, ackPolledIntegrationTask) + } + + Tuple pollAndCompleteLargePayloadTask(String taskName, String workerId, String outputPayloadPath) { + def polledIntegrationTask = workflowExecutionService.poll(taskName, workerId) + def ackPolledIntegrationTask = workflowExecutionService.ackTaskReceived(polledIntegrationTask.taskId) + def taskResult = new TaskResult(polledIntegrationTask) + taskResult.status = TaskResult.Status.COMPLETED + taskResult.outputData = null + taskResult.externalOutputPayloadStoragePath = outputPayloadPath + workflowExecutionService.updateTask(taskResult) + return new Tuple(polledIntegrationTask, ackPolledIntegrationTask) + } + + /** + * A helper method intended to be used in the then: block of the spock test feature, ideally intended to be called after either: + * pollAndCompleteTask function or pollAndFailTask function + * @param completedTaskAndAck A Tuple of polledTask and acknowledgement of the poll + * @param expectedTaskInputParams a map of input params that are verified against the polledTask that is part of the completedTaskAndAck tuple + */ + static void verifyPolledAndAcknowledgedTask(Tuple completedTaskAndAck, Map expectedTaskInputParams = null) { + assert completedTaskAndAck[0]: "The task polled cannot be null" + def polledIntegrationTask = completedTaskAndAck[0] as Task + def ackPolledIntegrationTask = completedTaskAndAck[1] as boolean + assert polledIntegrationTask + assert ackPolledIntegrationTask + if (expectedTaskInputParams) { + expectedTaskInputParams.forEach { + k, v -> + assert polledIntegrationTask.inputData.containsKey(k) + assert polledIntegrationTask.inputData[k] == v + } + } + } + + static void verifyPolledAndAcknowledgedLargePayloadTask(Tuple completedTaskAndAck) { + assert completedTaskAndAck[0]: "The task polled cannot be null" + def polledIntegrationTask = completedTaskAndAck[0] as Task + def ackPolledIntegrationTask = completedTaskAndAck[1] as boolean + assert polledIntegrationTask + assert ackPolledIntegrationTask + } +} diff --git a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java new file mode 100644 index 000000000..19d540f8c --- /dev/null +++ b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -0,0 +1,31 @@ +/* + *

+ * 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 com.netflix.conductor; + +import java.io.IOException; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** Copy of com.netflix.conductor.Conductor for use by @SpringBootTest in AbstractSpecification. */ + +// Prevents from the datasource beans to be loaded, AS they are needed only for specific databases. +// In case that SQL database is selected this class will be imported back in the appropriate +// database persistence module. +@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) +public class ConductorTestApp { + + public static void main(String[] args) throws IOException { + SpringApplication.run(ConductorTestApp.class, args); + } +} diff --git a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java new file mode 100644 index 000000000..226ca0594 --- /dev/null +++ b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -0,0 +1,27 @@ +/* + *

+ * 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 com.netflix.conductor.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** Supplies the standard Conductor {@link ObjectMapper} for tests that need them. */ +@Configuration +public class TestObjectMapperConfiguration { + + @Bean + public ObjectMapper testObjectMapper() { + return new ObjectMapperProvider().getObjectMapper(); + } +} diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java new file mode 100644 index 000000000..61e1103b4 --- /dev/null +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -0,0 +1,284 @@ +/* + *

+ * 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 com.netflix.conductor.test.integration; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.context.TestPropertySource; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +@TestPropertySource( + properties = {"conductor.indexing.enabled=true", "conductor.elasticsearch.version=6"}) +public abstract class AbstractEndToEndTest { + + private static final Logger log = LoggerFactory.getLogger(AbstractEndToEndTest.class); + + private static final String TASK_DEFINITION_PREFIX = "task_"; + private static final String DEFAULT_DESCRIPTION = "description"; + // Represents null value deserialized from the redis in memory db + private static final String DEFAULT_NULL_VALUE = "null"; + protected static final String DEFAULT_EMAIL_ADDRESS = "test@harness.com"; + + private static final ElasticsearchContainer container = + new ElasticsearchContainer( + DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag("6.8.12")); // this should match the client version + + private static RestClient restClient; + + // Initialization happens in a static block so the container is initialized + // only once for all the sub-class tests in a CI environment + // container is stopped when JVM exits + // https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers + static { + container.start(); + String httpHostAddress = container.getHttpHostAddress(); + System.setProperty("conductor.elasticsearch.url", "http://" + httpHostAddress); + log.info("Initialized Elasticsearch {}", container.getContainerId()); + } + + @BeforeClass + public static void initializeEs() { + String httpHostAddress = container.getHttpHostAddress(); + String host = httpHostAddress.split(":")[0]; + int port = Integer.parseInt(httpHostAddress.split(":")[1]); + + RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(host, port, "http")); + restClient = restClientBuilder.build(); + } + + @AfterClass + public static void cleanupEs() throws Exception { + // deletes all indices + Response beforeResponse = restClient.performRequest(new Request("GET", "/_cat/indices")); + Reader streamReader = new InputStreamReader(beforeResponse.getEntity().getContent()); + BufferedReader bufferedReader = new BufferedReader(streamReader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + String[] fields = line.split("\\s"); + String endpoint = String.format("/%s", fields[2]); + + restClient.performRequest(new Request("DELETE", endpoint)); + } + + if (restClient != null) { + restClient.close(); + } + } + + @Test + public void testEphemeralWorkflowsWithStoredTasks() { + String workflowExecutionName = "testEphemeralWorkflow"; + + createAndRegisterTaskDefinitions("storedTaskDef", 5); + WorkflowDef workflowDefinition = createWorkflowDefinition(workflowExecutionName); + WorkflowTask workflowTask1 = createWorkflowTask("storedTaskDef1"); + WorkflowTask workflowTask2 = createWorkflowTask("storedTaskDef2"); + workflowDefinition.getTasks().addAll(Arrays.asList(workflowTask1, workflowTask2)); + + String workflowId = startWorkflow(workflowExecutionName, workflowDefinition); + assertNotNull(workflowId); + + Workflow workflow = getWorkflow(workflowId, true); + WorkflowDef ephemeralWorkflow = workflow.getWorkflowDefinition(); + assertNotNull(ephemeralWorkflow); + assertEquals(workflowDefinition, ephemeralWorkflow); + } + + @Test + public void testEphemeralWorkflowsWithEphemeralTasks() { + String workflowExecutionName = "ephemeralWorkflowWithEphemeralTasks"; + + WorkflowDef workflowDefinition = createWorkflowDefinition(workflowExecutionName); + WorkflowTask workflowTask1 = createWorkflowTask("ephemeralTask1"); + TaskDef taskDefinition1 = createTaskDefinition("ephemeralTaskDef1"); + workflowTask1.setTaskDefinition(taskDefinition1); + WorkflowTask workflowTask2 = createWorkflowTask("ephemeralTask2"); + TaskDef taskDefinition2 = createTaskDefinition("ephemeralTaskDef2"); + workflowTask2.setTaskDefinition(taskDefinition2); + workflowDefinition.getTasks().addAll(Arrays.asList(workflowTask1, workflowTask2)); + + String workflowId = startWorkflow(workflowExecutionName, workflowDefinition); + + assertNotNull(workflowId); + + Workflow workflow = getWorkflow(workflowId, true); + WorkflowDef ephemeralWorkflow = workflow.getWorkflowDefinition(); + assertNotNull(ephemeralWorkflow); + assertEquals(workflowDefinition, ephemeralWorkflow); + + List ephemeralTasks = ephemeralWorkflow.getTasks(); + assertEquals(2, ephemeralTasks.size()); + for (WorkflowTask ephemeralTask : ephemeralTasks) { + assertNotNull(ephemeralTask.getTaskDefinition()); + } + } + + @Test + public void testEphemeralWorkflowsWithEphemeralAndStoredTasks() { + createAndRegisterTaskDefinitions("storedTask", 1); + + WorkflowDef workflowDefinition = + createWorkflowDefinition("testEphemeralWorkflowsWithEphemeralAndStoredTasks"); + + WorkflowTask workflowTask1 = createWorkflowTask("ephemeralTask1"); + TaskDef taskDefinition1 = createTaskDefinition("ephemeralTaskDef1"); + workflowTask1.setTaskDefinition(taskDefinition1); + + WorkflowTask workflowTask2 = createWorkflowTask("storedTask0"); + + workflowDefinition.getTasks().add(workflowTask1); + workflowDefinition.getTasks().add(workflowTask2); + + String workflowExecutionName = "ephemeralWorkflowWithEphemeralAndStoredTasks"; + + String workflowId = startWorkflow(workflowExecutionName, workflowDefinition); + assertNotNull(workflowId); + + Workflow workflow = getWorkflow(workflowId, true); + WorkflowDef ephemeralWorkflow = workflow.getWorkflowDefinition(); + assertNotNull(ephemeralWorkflow); + assertEquals(workflowDefinition, ephemeralWorkflow); + + TaskDef storedTaskDefinition = getTaskDefinition("storedTask0"); + List tasks = ephemeralWorkflow.getTasks(); + assertEquals(2, tasks.size()); + assertEquals(workflowTask1, tasks.get(0)); + TaskDef currentStoredTaskDefinition = tasks.get(1).getTaskDefinition(); + assertNotNull(currentStoredTaskDefinition); + assertEquals(storedTaskDefinition, currentStoredTaskDefinition); + } + + @Test + public void testEventHandler() { + String eventName = "conductor:test_workflow:complete_task_with_event"; + EventHandler eventHandler = new EventHandler(); + eventHandler.setName("test_complete_task_event"); + EventHandler.Action completeTaskAction = new EventHandler.Action(); + completeTaskAction.setAction(EventHandler.Action.Type.complete_task); + completeTaskAction.setComplete_task(new EventHandler.TaskDetails()); + completeTaskAction.getComplete_task().setTaskRefName("test_task"); + completeTaskAction.getComplete_task().setWorkflowId("test_id"); + completeTaskAction.getComplete_task().setOutput(new HashMap<>()); + eventHandler.getActions().add(completeTaskAction); + eventHandler.setEvent(eventName); + eventHandler.setActive(true); + registerEventHandler(eventHandler); + + Iterator it = getEventHandlers(eventName, true); + EventHandler result = it.next(); + assertFalse(it.hasNext()); + assertEquals(eventHandler.getName(), result.getName()); + } + + protected WorkflowTask createWorkflowTask(String name) { + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName(name); + workflowTask.setWorkflowTaskType(TaskType.SIMPLE); + workflowTask.setTaskReferenceName(name); + workflowTask.setDescription(getDefaultDescription(name)); + workflowTask.setDynamicTaskNameParam(DEFAULT_NULL_VALUE); + workflowTask.setCaseValueParam(DEFAULT_NULL_VALUE); + workflowTask.setCaseExpression(DEFAULT_NULL_VALUE); + workflowTask.setDynamicForkTasksParam(DEFAULT_NULL_VALUE); + workflowTask.setDynamicForkTasksInputParamName(DEFAULT_NULL_VALUE); + workflowTask.setSink(DEFAULT_NULL_VALUE); + workflowTask.setEvaluatorType(DEFAULT_NULL_VALUE); + workflowTask.setExpression(DEFAULT_NULL_VALUE); + return workflowTask; + } + + protected TaskDef createTaskDefinition(String name) { + TaskDef taskDefinition = new TaskDef(); + taskDefinition.setName(name); + return taskDefinition; + } + + protected WorkflowDef createWorkflowDefinition(String workflowName) { + WorkflowDef workflowDefinition = new WorkflowDef(); + workflowDefinition.setName(workflowName); + workflowDefinition.setDescription(getDefaultDescription(workflowName)); + workflowDefinition.setFailureWorkflow(DEFAULT_NULL_VALUE); + workflowDefinition.setOwnerEmail(DEFAULT_EMAIL_ADDRESS); + return workflowDefinition; + } + + protected List createAndRegisterTaskDefinitions( + String prefixTaskDefinition, int numberOfTaskDefinitions) { + String prefix = Optional.ofNullable(prefixTaskDefinition).orElse(TASK_DEFINITION_PREFIX); + List definitions = new LinkedList<>(); + for (int i = 0; i < numberOfTaskDefinitions; i++) { + TaskDef def = + new TaskDef( + prefix + i, + "task " + i + DEFAULT_DESCRIPTION, + DEFAULT_EMAIL_ADDRESS, + 3, + 60, + 60); + def.setTimeoutPolicy(TaskDef.TimeoutPolicy.RETRY); + definitions.add(def); + } + this.registerTaskDefinitions(definitions); + return definitions; + } + + private String getDefaultDescription(String nameResource) { + return nameResource + " " + DEFAULT_DESCRIPTION; + } + + protected abstract String startWorkflow( + String workflowExecutionName, WorkflowDef workflowDefinition); + + protected abstract Workflow getWorkflow(String workflowId, boolean includeTasks); + + protected abstract TaskDef getTaskDefinition(String taskName); + + protected abstract void registerTaskDefinitions(List taskDefinitionList); + + protected abstract void registerWorkflowDefinition(WorkflowDef workflowDefinition); + + protected abstract void registerEventHandler(EventHandler eventHandler); + + protected abstract Iterator getEventHandlers(String event, boolean activeOnly); +} diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java new file mode 100644 index 000000000..b5cbcc2e1 --- /dev/null +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -0,0 +1,252 @@ +/* + *

+ * 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 com.netflix.conductor.test.integration.grpc; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.client.grpc.EventClient; +import com.netflix.conductor.client.grpc.MetadataClient; +import com.netflix.conductor.client.grpc.TaskClient; +import com.netflix.conductor.client.grpc.WorkflowClient; +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.Task.Status; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskDef.TimeoutPolicy; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.Workflow.WorkflowStatus; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.test.integration.AbstractEndToEndTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringRunner.class) +@SpringBootTest( + properties = {"conductor.grpc-server.enabled=true", "conductor.grpc-server.port=8092"}) +@TestPropertySource(locations = "classpath:application-integrationtest.properties") +public abstract class AbstractGrpcEndToEndTest extends AbstractEndToEndTest { + + protected static TaskClient taskClient; + protected static WorkflowClient workflowClient; + protected static MetadataClient metadataClient; + protected static EventClient eventClient; + + @Override + protected String startWorkflow(String workflowExecutionName, WorkflowDef workflowDefinition) { + StartWorkflowRequest workflowRequest = + new StartWorkflowRequest() + .withName(workflowExecutionName) + .withWorkflowDef(workflowDefinition); + return workflowClient.startWorkflow(workflowRequest); + } + + @Override + protected Workflow getWorkflow(String workflowId, boolean includeTasks) { + return workflowClient.getWorkflow(workflowId, includeTasks); + } + + @Override + protected TaskDef getTaskDefinition(String taskName) { + return metadataClient.getTaskDef(taskName); + } + + @Override + protected void registerTaskDefinitions(List taskDefinitionList) { + metadataClient.registerTaskDefs(taskDefinitionList); + } + + @Override + protected void registerWorkflowDefinition(WorkflowDef workflowDefinition) { + metadataClient.registerWorkflowDef(workflowDefinition); + } + + @Override + protected void registerEventHandler(EventHandler eventHandler) { + eventClient.registerEventHandler(eventHandler); + } + + @Override + protected Iterator getEventHandlers(String event, boolean activeOnly) { + return eventClient.getEventHandlers(event, activeOnly); + } + + @Test + public void testAll() throws Exception { + assertNotNull(taskClient); + List defs = new LinkedList<>(); + for (int i = 0; i < 5; i++) { + TaskDef def = new TaskDef("t" + i, "task " + i, DEFAULT_EMAIL_ADDRESS, 3, 60, 60); + def.setTimeoutPolicy(TimeoutPolicy.RETRY); + defs.add(def); + } + metadataClient.registerTaskDefs(defs); + + for (int i = 0; i < 5; i++) { + final String taskName = "t" + i; + TaskDef def = metadataClient.getTaskDef(taskName); + assertNotNull(def); + assertEquals(taskName, def.getName()); + } + + WorkflowDef def = createWorkflowDefinition("test"); + WorkflowTask t0 = createWorkflowTask("t0"); + WorkflowTask t1 = createWorkflowTask("t1"); + + def.getTasks().add(t0); + def.getTasks().add(t1); + + metadataClient.registerWorkflowDef(def); + WorkflowDef found = metadataClient.getWorkflowDef(def.getName(), null); + assertNotNull(found); + assertEquals(def, found); + + String correlationId = "test_corr_id"; + StartWorkflowRequest startWf = new StartWorkflowRequest(); + startWf.setName(def.getName()); + startWf.setCorrelationId(correlationId); + + String workflowId = workflowClient.startWorkflow(startWf); + assertNotNull(workflowId); + + Workflow workflow = workflowClient.getWorkflow(workflowId, false); + assertEquals(0, workflow.getTasks().size()); + assertEquals(workflowId, workflow.getWorkflowId()); + + workflow = workflowClient.getWorkflow(workflowId, true); + assertNotNull(workflow); + assertEquals(WorkflowStatus.RUNNING, workflow.getStatus()); + assertEquals(1, workflow.getTasks().size()); + assertEquals(t0.getTaskReferenceName(), workflow.getTasks().get(0).getReferenceTaskName()); + assertEquals(workflowId, workflow.getWorkflowId()); + + List runningIds = + workflowClient.getRunningWorkflow(def.getName(), def.getVersion()); + assertNotNull(runningIds); + assertEquals(1, runningIds.size()); + assertEquals(workflowId, runningIds.get(0)); + + List polled = + taskClient.batchPollTasksByTaskType("non existing task", "test", 1, 100); + assertNotNull(polled); + assertEquals(0, polled.size()); + + polled = taskClient.batchPollTasksByTaskType(t0.getName(), "test", 1, 100); + assertNotNull(polled); + assertEquals(1, polled.size()); + assertEquals(t0.getName(), polled.get(0).getTaskDefName()); + Task task = polled.get(0); + + task.getOutputData().put("key1", "value1"); + task.setStatus(Status.COMPLETED); + taskClient.updateTask(new TaskResult(task)); + + polled = taskClient.batchPollTasksByTaskType(t0.getName(), "test", 1, 100); + assertNotNull(polled); + assertTrue(polled.toString(), polled.isEmpty()); + + workflow = workflowClient.getWorkflow(workflowId, true); + assertNotNull(workflow); + assertEquals(WorkflowStatus.RUNNING, workflow.getStatus()); + assertEquals(2, workflow.getTasks().size()); + assertEquals(t0.getTaskReferenceName(), workflow.getTasks().get(0).getReferenceTaskName()); + assertEquals(t1.getTaskReferenceName(), workflow.getTasks().get(1).getReferenceTaskName()); + assertEquals(Status.COMPLETED, workflow.getTasks().get(0).getStatus()); + assertEquals(Status.SCHEDULED, workflow.getTasks().get(1).getStatus()); + + Task taskById = taskClient.getTaskDetails(task.getTaskId()); + assertNotNull(taskById); + assertEquals(task.getTaskId(), taskById.getTaskId()); + + Thread.sleep(1000); + SearchResult searchResult = + workflowClient.search("workflowType='" + def.getName() + "'"); + assertNotNull(searchResult); + assertEquals(1, searchResult.getTotalHits()); + assertEquals(workflow.getWorkflowId(), searchResult.getResults().get(0).getWorkflowId()); + + SearchResult searchResultV2 = + workflowClient.searchV2("workflowType='" + def.getName() + "'"); + assertNotNull(searchResultV2); + assertEquals(1, searchResultV2.getTotalHits()); + assertEquals(workflow.getWorkflowId(), searchResultV2.getResults().get(0).getWorkflowId()); + + SearchResult searchResultAdvanced = + workflowClient.search(0, 1, null, null, "workflowType='" + def.getName() + "'"); + assertNotNull(searchResultAdvanced); + assertEquals(1, searchResultAdvanced.getTotalHits()); + assertEquals( + workflow.getWorkflowId(), searchResultAdvanced.getResults().get(0).getWorkflowId()); + + SearchResult searchResultV2Advanced = + workflowClient.searchV2(0, 1, null, null, "workflowType='" + def.getName() + "'"); + assertNotNull(searchResultV2Advanced); + assertEquals(1, searchResultV2Advanced.getTotalHits()); + assertEquals( + workflow.getWorkflowId(), + searchResultV2Advanced.getResults().get(0).getWorkflowId()); + + SearchResult taskSearchResult = + taskClient.search("taskType='" + t0.getName() + "'"); + assertNotNull(taskSearchResult); + assertEquals(1, searchResultV2Advanced.getTotalHits()); + assertEquals(t0.getName(), taskSearchResult.getResults().get(0).getTaskDefName()); + + SearchResult taskSearchResultAdvanced = + taskClient.search(0, 1, null, null, "taskType='" + t0.getName() + "'"); + assertNotNull(taskSearchResultAdvanced); + assertEquals(1, taskSearchResultAdvanced.getTotalHits()); + assertEquals(t0.getName(), taskSearchResultAdvanced.getResults().get(0).getTaskDefName()); + + SearchResult taskSearchResultV2 = + taskClient.searchV2("taskType='" + t0.getName() + "'"); + assertNotNull(taskSearchResultV2); + assertEquals(1, searchResultV2Advanced.getTotalHits()); + assertEquals( + t0.getTaskReferenceName(), + taskSearchResultV2.getResults().get(0).getReferenceTaskName()); + + SearchResult taskSearchResultV2Advanced = + taskClient.searchV2(0, 1, null, null, "taskType='" + t0.getName() + "'"); + assertNotNull(taskSearchResultV2Advanced); + assertEquals(1, taskSearchResultV2Advanced.getTotalHits()); + assertEquals( + t0.getTaskReferenceName(), + taskSearchResultV2Advanced.getResults().get(0).getReferenceTaskName()); + + workflowClient.terminateWorkflow(workflowId, "terminate reason"); + workflow = workflowClient.getWorkflow(workflowId, true); + assertNotNull(workflow); + assertEquals(WorkflowStatus.TERMINATED, workflow.getStatus()); + + workflowClient.restart(workflowId, false); + workflow = workflowClient.getWorkflow(workflowId, true); + assertNotNull(workflow); + assertEquals(WorkflowStatus.RUNNING, workflow.getStatus()); + assertEquals(1, workflow.getTasks().size()); + } +} diff --git a/test-util/src/test/resources/application-integrationtest.properties b/test-util/src/test/resources/application-integrationtest.properties new file mode 100644 index 000000000..3b9e396ae --- /dev/null +++ b/test-util/src/test/resources/application-integrationtest.properties @@ -0,0 +1,41 @@ +conductor.db.type=memory +conductor.workflow-execution-lock.type=local_only +conductor.external-payload-storage.type=dummy +conductor.indexing.enabled=true +conductor.indexing.type=postgres + +conductor.app.stack=test +conductor.app.appId=conductor + +conductor.app.workflow-offset-timeout=30s + +conductor.system-task-workers.enabled=false +conductor.app.system-task-worker-callback-duration=0 + +conductor.app.event-message-indexing-enabled=true +conductor.app.event-execution-indexing-enabled=true + +conductor.workflow-reconciler.enabled=true +conductor.workflow-repair-service.enabled=false + +conductor.app.workflow-execution-lock-enabled=false + +conductor.app.workflow-input-payload-size-threshold=10KB +conductor.app.max-workflow-input-payload-size-threshold=10240KB +conductor.app.workflow-output-payload-size-threshold=10KB +conductor.app.max-workflow-output-payload-size-threshold=10240KB +conductor.app.task-input-payload-size-threshold=10KB +conductor.app.max-task-input-payload-size-threshold=10240KB +conductor.app.task-output-payload-size-threshold=10KB +conductor.app.max-task-output-payload-size-threshold=10240KB +conductor.app.max-workflow-variables-payload-size-threshold=2KB + +conductor.redis.availability-zone=us-east-1c +conductor.redis.data-center-region=us-east-1 +conductor.redis.workflow-namespace-prefix=integration-test +conductor.redis.queue-namespace-prefix=integtest + +conductor.elasticsearch.index-prefix=conductor +conductor.elasticsearch.cluster-health-color=yellow + +management.metrics.export.datadog.enabled=false diff --git a/workflow-event-listener/README.md b/workflow-event-listener/README.md new file mode 100644 index 000000000..8dcc596bc --- /dev/null +++ b/workflow-event-listener/README.md @@ -0,0 +1,49 @@ +# Workflow Event Listeners +Workflow Event listeners can be configured for the purpose in Conductor: +1. Remove and/or archive workflows from primary datasource (e.g. Redis) once the workflow reaches a terminal status +2. Publish a message to a conductor queue as the workflows complete that can be used to trigger other workflows + +## Published Artifacts + +Group: `com.netflix.conductor` + +| Published Artifact | Description | +| ----------- | ----------- | +| conductor-workflow-event-listener | Event Listeners for Conductor | + +## Backward Compatibility +Workflow event listeners are part of `conductor-contribs` binary as well - if you are already consuming contribs module as part of your build, +you do not need to add this as a separate dependency. +Core conductor-server also includes event listeners via contribs dependency. + +## Configuration + +### Workflow Archival +Set the following properties to archive the workflows as they complete. +When archived, the workflow execution is removed from the primary DAO and pushed to index store (e.g. Elasticsearch) +```properties +conductor.workflow-status-listener.type=archive + +#when non-zero, workflows are removed from the primary storage after the TTL expiry +conductor.workflow-status-listener.archival.ttlDuration=0 + +#number of threads for the background worker that processes the archival request +conductor.workflow-status-listener.archival.delayQueueWorkerThreadCount=5 +``` + +### Queue publisher +Publish a summary of workflow [WorkflowSummary](https://github.com/Netflix/conductor/blob/main/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java) +to a queue as the workflow gets completed. + +```properties +conductor.workflow-status-listener.type=queue_publisher + +#Queue for successful completion of a workflow +conductor.workflow-status-listener.queue-publisher.successQueue=_callbackSuccessQueue + +#Queue for failed workflows +conductor.workflow-status-listener.queue-publisher.failureQueue=_callbackFailureQueue + +#Queue for terminal state workflows (success or failed) +conductor.workflow-status-listener.queue-publisher.finalizeQueue=_callbackFinalizeQueue +``` diff --git a/workflow-event-listener/build.gradle b/workflow-event-listener/build.gradle new file mode 100644 index 000000000..840bff300 --- /dev/null +++ b/workflow-event-listener/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'groovy' +} +dependencies { + + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" + testImplementation "org.spockframework:spock-core:${revSpock}" + testImplementation "org.spockframework:spock-spring:${revSpock}" + + implementation "org.springframework.boot:spring-boot-starter-log4j2" + + + implementation project(':conductor-server') + testImplementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation project(':conductor-test-util').sourceSets.test.output + + //In memory + implementation "org.rarefiedredis.redis:redis-java:${revRarefiedRedis}" +} \ No newline at end of file diff --git a/workflow-event-listener/dependencies.lock b/workflow-event-listener/dependencies.lock new file mode 100644 index 000000000..88170b10d --- /dev/null +++ b/workflow-event-listener/dependencies.lock @@ -0,0 +1,1170 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "3.1.4" + } + }, + "compileClasspath": { + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.rarefiedredis.redis:redis-java": { + "locked": "0.0.17" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "3.1.4" + } + }, + "runtimeClasspath": { + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "32.0.1-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc" + ], + "locked": "3.22.3" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "8.7.1" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + }, + "testCompileClasspath": { + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.rarefiedredis.redis:redis-java": { + "locked": "0.0.17" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "3.1.4" + } + }, + "testRuntimeClasspath": { + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "32.0.1-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc" + ], + "locked": "3.22.3" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "8.7.1" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + } +} \ No newline at end of file diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java new file mode 100644 index 000000000..12ac5d268 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java @@ -0,0 +1,134 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.archive; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + + +import jakarta.annotation.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.dal.ExecutionDAOFacade; +import com.netflix.conductor.core.listener.WorkflowStatusListener; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.model.WorkflowModel; + +public class ArchivingWithTTLWorkflowStatusListener implements WorkflowStatusListener { + + private static final Logger LOGGER = + LoggerFactory.getLogger(ArchivingWithTTLWorkflowStatusListener.class); + + private final ExecutionDAOFacade executionDAOFacade; + private final int archiveTTLSeconds; + private final int delayArchiveSeconds; + private final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; + + public ArchivingWithTTLWorkflowStatusListener( + ExecutionDAOFacade executionDAOFacade, ArchivingWorkflowListenerProperties properties) { + this.executionDAOFacade = executionDAOFacade; + this.archiveTTLSeconds = (int) properties.getTtlDuration().getSeconds(); + this.delayArchiveSeconds = properties.getWorkflowArchivalDelay(); + + this.scheduledThreadPoolExecutor = + new ScheduledThreadPoolExecutor( + properties.getDelayQueueWorkerThreadCount(), + (runnable, executor) -> { + LOGGER.warn( + "Request {} to delay archiving index dropped in executor {}", + runnable, + executor); + Monitors.recordDiscardedArchivalCount(); + }); + this.scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); + LOGGER.warn( + "Workflow removal with TTL is no longer supported, " + + "when using this class, workflows will be removed immediately"); + } + + @PreDestroy + public void shutdownExecutorService() { + try { + LOGGER.info("Gracefully shutdown executor service"); + scheduledThreadPoolExecutor.shutdown(); + if (scheduledThreadPoolExecutor.awaitTermination( + delayArchiveSeconds, TimeUnit.SECONDS)) { + LOGGER.debug("tasks completed, shutting down"); + } else { + LOGGER.warn("Forcing shutdown after waiting for {} seconds", delayArchiveSeconds); + scheduledThreadPoolExecutor.shutdownNow(); + } + } catch (InterruptedException ie) { + LOGGER.warn( + "Shutdown interrupted, invoking shutdownNow on scheduledThreadPoolExecutor for delay queue"); + scheduledThreadPoolExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + public void onWorkflowCompleted(WorkflowModel workflow) { + LOGGER.info("Archiving workflow {} on completion ", workflow.getWorkflowId()); + if (delayArchiveSeconds > 0) { + scheduledThreadPoolExecutor.schedule( + new DelayArchiveWorkflow(workflow, executionDAOFacade), + delayArchiveSeconds, + TimeUnit.SECONDS); + } else { + this.executionDAOFacade.removeWorkflow(workflow.getWorkflowId(), true); + Monitors.recordWorkflowArchived(workflow.getWorkflowName(), workflow.getStatus()); + } + } + + @Override + public void onWorkflowTerminated(WorkflowModel workflow) { + LOGGER.info("Archiving workflow {} on termination", workflow.getWorkflowId()); + if (delayArchiveSeconds > 0) { + scheduledThreadPoolExecutor.schedule( + new DelayArchiveWorkflow(workflow, executionDAOFacade), + delayArchiveSeconds, + TimeUnit.SECONDS); + } else { + this.executionDAOFacade.removeWorkflow(workflow.getWorkflowId(), true); + Monitors.recordWorkflowArchived(workflow.getWorkflowName(), workflow.getStatus()); + } + } + + private class DelayArchiveWorkflow implements Runnable { + + private final String workflowId; + private final String workflowName; + private final WorkflowModel.Status status; + private final ExecutionDAOFacade executionDAOFacade; + + DelayArchiveWorkflow(WorkflowModel workflow, ExecutionDAOFacade executionDAOFacade) { + this.workflowId = workflow.getWorkflowId(); + this.workflowName = workflow.getWorkflowName(); + this.status = workflow.getStatus(); + this.executionDAOFacade = executionDAOFacade; + } + + @Override + public void run() { + try { + this.executionDAOFacade.removeWorkflow(workflowId, true); + LOGGER.info("Archived workflow {}", workflowId); + Monitors.recordWorkflowArchived(workflowName, status); + Monitors.recordArchivalDelayQueueSize( + scheduledThreadPoolExecutor.getQueue().size()); + } catch (Exception e) { + LOGGER.error("Unable to archive workflow: {}", workflowId, e); + } + } + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java new file mode 100644 index 000000000..b6de249cb --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java @@ -0,0 +1,36 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.archive; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.core.dal.ExecutionDAOFacade; +import com.netflix.conductor.core.listener.WorkflowStatusListener; + +@Configuration +@EnableConfigurationProperties(ArchivingWorkflowListenerProperties.class) +@ConditionalOnProperty(name = "conductor.workflow-status-listener.type", havingValue = "archive") +public class ArchivingWorkflowListenerConfiguration { + + @Bean + public WorkflowStatusListener getWorkflowStatusListener( + ExecutionDAOFacade executionDAOFacade, ArchivingWorkflowListenerProperties properties) { + if (properties.getTtlDuration().getSeconds() > 0) { + return new ArchivingWithTTLWorkflowStatusListener(executionDAOFacade, properties); + } else { + return new ArchivingWorkflowStatusListener(executionDAOFacade); + } + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java new file mode 100644 index 000000000..f687516f3 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java @@ -0,0 +1,66 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.archive; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; +import org.springframework.core.env.Environment; + +@ConfigurationProperties("conductor.workflow-status-listener.archival") +public class ArchivingWorkflowListenerProperties { + + private final Environment environment; + + @Autowired + public ArchivingWorkflowListenerProperties(Environment environment) { + this.environment = environment; + } + + /** + * The time to live in seconds for workflow archiving module. Currently, only RedisExecutionDAO + * supports this + */ + @DurationUnit(ChronoUnit.SECONDS) + private Duration ttlDuration = Duration.ZERO; + + /** The number of threads to process the delay queue in workflow archival */ + private int delayQueueWorkerThreadCount = 5; + + public Duration getTtlDuration() { + return ttlDuration; + } + + public void setTtlDuration(Duration ttlDuration) { + this.ttlDuration = ttlDuration; + } + + public int getDelayQueueWorkerThreadCount() { + return delayQueueWorkerThreadCount; + } + + public void setDelayQueueWorkerThreadCount(int delayQueueWorkerThreadCount) { + this.delayQueueWorkerThreadCount = delayQueueWorkerThreadCount; + } + + /** The time to delay the archival of workflow */ + public int getWorkflowArchivalDelay() { + return environment.getProperty( + "conductor.workflow-status-listener.archival.delaySeconds", + Integer.class, + environment.getProperty( + "conductor.app.asyncUpdateDelaySeconds", Integer.class, 60)); + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java new file mode 100644 index 000000000..9016d33f4 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java @@ -0,0 +1,51 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.archive; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.dal.ExecutionDAOFacade; +import com.netflix.conductor.core.listener.WorkflowStatusListener; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.model.WorkflowModel; + +/** + * Provides default implementation of workflow archiving immediately after workflow is completed or + * terminated. + * + * @author pavel.halabala + */ +public class ArchivingWorkflowStatusListener implements WorkflowStatusListener { + + private static final Logger LOGGER = + LoggerFactory.getLogger(ArchivingWorkflowStatusListener.class); + private final ExecutionDAOFacade executionDAOFacade; + + public ArchivingWorkflowStatusListener(ExecutionDAOFacade executionDAOFacade) { + this.executionDAOFacade = executionDAOFacade; + } + + @Override + public void onWorkflowCompleted(WorkflowModel workflow) { + LOGGER.info("Archiving workflow {} on completion ", workflow.getWorkflowId()); + this.executionDAOFacade.removeWorkflow(workflow.getWorkflowId(), true); + Monitors.recordWorkflowArchived(workflow.getWorkflowName(), workflow.getStatus()); + } + + @Override + public void onWorkflowTerminated(WorkflowModel workflow) { + LOGGER.info("Archiving workflow {} on termination", workflow.getWorkflowId()); + this.executionDAOFacade.removeWorkflow(workflow.getWorkflowId(), true); + Monitors.recordWorkflowArchived(workflow.getWorkflowName(), workflow.getStatus()); + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java new file mode 100644 index 000000000..75b3166a3 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java @@ -0,0 +1,84 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.conductorqueue; + +import java.util.Collections; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.listener.WorkflowStatusListener; +import com.netflix.conductor.dao.QueueDAO; +import com.netflix.conductor.model.WorkflowModel; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Publishes a {@link Message} containing a {@link WorkflowSummary} to the undlerying {@link + * QueueDAO} implementation on a workflow completion or termination event. + */ +public class ConductorQueueStatusPublisher implements WorkflowStatusListener { + + private static final Logger LOGGER = + LoggerFactory.getLogger(ConductorQueueStatusPublisher.class); + private final QueueDAO queueDAO; + private final ObjectMapper objectMapper; + + private final String successStatusQueue; + private final String failureStatusQueue; + private final String finalizeStatusQueue; + + public ConductorQueueStatusPublisher( + QueueDAO queueDAO, + ObjectMapper objectMapper, + ConductorQueueStatusPublisherProperties properties) { + this.queueDAO = queueDAO; + this.objectMapper = objectMapper; + this.successStatusQueue = properties.getSuccessQueue(); + this.failureStatusQueue = properties.getFailureQueue(); + this.finalizeStatusQueue = properties.getFinalizeQueue(); + } + + @Override + public void onWorkflowCompleted(WorkflowModel workflow) { + LOGGER.info("Publishing callback of workflow {} on completion ", workflow.getWorkflowId()); + queueDAO.push(successStatusQueue, Collections.singletonList(workflowToMessage(workflow))); + } + + @Override + public void onWorkflowTerminated(WorkflowModel workflow) { + LOGGER.info("Publishing callback of workflow {} on termination", workflow.getWorkflowId()); + queueDAO.push(failureStatusQueue, Collections.singletonList(workflowToMessage(workflow))); + } + + @Override + public void onWorkflowFinalized(WorkflowModel workflow) { + LOGGER.info("Publishing callback of workflow {} on finalization", workflow.getWorkflowId()); + queueDAO.push(finalizeStatusQueue, Collections.singletonList(workflowToMessage(workflow))); + } + + private Message workflowToMessage(WorkflowModel workflowModel) { + String jsonWfSummary; + WorkflowSummary summary = new WorkflowSummary(workflowModel.toWorkflow()); + try { + jsonWfSummary = objectMapper.writeValueAsString(summary); + } catch (JsonProcessingException e) { + LOGGER.error( + "Failed to convert WorkflowSummary: {} to String. Exception: {}", summary, e); + throw new RuntimeException(e); + } + return new Message(workflowModel.getWorkflowId(), jsonWfSummary, null); + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java new file mode 100644 index 000000000..83a32fe10 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java @@ -0,0 +1,38 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.conductorqueue; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.core.listener.WorkflowStatusListener; +import com.netflix.conductor.dao.QueueDAO; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Configuration +@EnableConfigurationProperties(ConductorQueueStatusPublisherProperties.class) +@ConditionalOnProperty( + name = "conductor.workflow-status-listener.type", + havingValue = "queue_publisher") +public class ConductorQueueStatusPublisherConfiguration { + + @Bean + public WorkflowStatusListener getWorkflowStatusListener( + QueueDAO queueDAO, + ConductorQueueStatusPublisherProperties properties, + ObjectMapper objectMapper) { + return new ConductorQueueStatusPublisher(queueDAO, objectMapper, properties); + } +} diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java new file mode 100644 index 000000000..522279385 --- /dev/null +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java @@ -0,0 +1,48 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener.conductorqueue; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("conductor.workflow-status-listener.queue-publisher") +public class ConductorQueueStatusPublisherProperties { + + private String successQueue = "_callbackSuccessQueue"; + + private String failureQueue = "_callbackFailureQueue"; + + private String finalizeQueue = "_callbackFinalizeQueue"; + + public String getSuccessQueue() { + return successQueue; + } + + public void setSuccessQueue(String successQueue) { + this.successQueue = successQueue; + } + + public String getFailureQueue() { + return failureQueue; + } + + public void setFailureQueue(String failureQueue) { + this.failureQueue = failureQueue; + } + + public String getFinalizeQueue() { + return finalizeQueue; + } + + public void setFinalizeQueue(String finalizeQueue) { + this.finalizeQueue = finalizeQueue; + } +} diff --git a/workflow-event-listener/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/workflow-event-listener/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..3156ecbef --- /dev/null +++ b/workflow-event-listener/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,136 @@ +{ + "properties": [ + { + "name": "conductor.metrics-logger.reportPeriodSeconds", + "type": "java.lang.Long", + "description": "The interval (in seconds) at which the metrics will be reported into the log stream by the metrics-logger." + }, + { + "name": "conductor.tasks.http.readTimeout", + "type": "java.lang.Integer", + "description": "The read timeout of the underlying HttpClient used by the HTTP task." + }, + { + "name": "conductor.tasks.http.connectTimeout", + "type": "java.lang.Integer", + "description": "The connection timeout of the underlying HttpClient used by the HTTP task." + }, + { + "name": "conductor.tasks.kafka-publish.requestTimeoutMs", + "type": "java.lang.String", + "description": "The request.timeout.ms value that the kafka producer is configured with in the KAFKA_PUBLISH task." + }, + { + "name": "conductor.tasks.kafka-publish.maxBlockMs", + "type": "java.lang.String", + "description": "The max.block.ms value that the kafka producer is configured with in the KAFKA_PUBLISH task." + }, + { + "name": "conductor.tasks.kafka-publish.cacheSize", + "type": "java.lang.Integer", + "description": "The maximum number of entries permitted in the in-memory cache used by the KAFKA_PUBLISH task." + }, + { + "name": "conductor.tasks.kafka-publish.cacheTimeMs", + "type": "java.lang.Integer", + "description": "The duration after which a cached entry will be removed from the in-memory cache used by the KAFKA_PUBLISH task." + }, + { + "name": "conductor.workflow-status-listener.type", + "type": "java.lang.String", + "description": "The implementation of the workflow status listener to be used." + }, + { + "name": "conductor.workflow-execution-lock.type", + "type": "java.lang.String", + "description": "The implementation of the workflow execution lock to be used.", + "defaultValue": "noop_lock" + }, + { + "name": "conductor.event-queues.sqs.enabled", + "type": "java.lang.Boolean", + "description": "Enable the use of AWS SQS implementation to provide queues for consuming events.", + "sourceType": "com.netflix.conductor.contribs.queue.sqs.config.SQSEventQueueConfiguration" + }, + { + "name": "conductor.event-queues.amqp.enabled", + "type": "java.lang.Boolean", + "description": "Enable the use of RabbitMQ implementation to provide queues for consuming events.", + "sourceType": "com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueConfiguration" + }, + { + "name": "conductor.event-queues.nats.enabled", + "type": "java.lang.Boolean", + "description": "Enable the use of NATS implementation to provide queues for consuming events.", + "sourceType": "com.netflix.conductor.contribs.queue.nats.config.NATSConfiguration" + }, + { + "name": "conductor.event-queues.nats-stream.enabled", + "type": "java.lang.Boolean", + "description": "Enable the use of NATS Streaming implementation to provide queues for consuming events.", + "sourceType": "com.netflix.conductor.contribs.queue.nats.config.NATSStreamConfiguration" + }, + { + "name": "conductor.default-event-queue.type", + "type": "java.lang.String", + "description": "The default event queue type to listen on for the WAIT task." + } + ], + "hints": [ + { + "name": "conductor.workflow-status-listener.type", + "values": [ + { + "value": "stub", + "description": "Use the no-op implementation of the workflow status listener." + }, + { + "value": "archive", + "description": "Use then archive implementation which immediately archives the workflow upon termination or completion as the workflow status listener." + }, + { + "value": "queue_publisher", + "description": "Use the publisher implementation which publishes a message to the underlying queue implementation upon termination or completion as the workflow status listener." + } + ] + }, + { + "name": "conductor.default-event-queue.type", + "values": [ + { + "value": "sqs", + "description": "Use AWS SQS as the event queue to listen on for the WAIT task." + }, + { + "value": "amqp", + "description": "Use RabbitMQ as the event queue to listen on for the WAIT task." + }, + { + "value": "nats_stream", + "description": "Use NATS Stream as the event queue to listen on for the WAIT task." + } + ] + }, + { + "name": "conductor.workflow-execution-lock.type", + "values": [ + { + "value": "noop_lock", + "description": "Use the no-op implementation as the lock provider." + }, + { + "value": "local_only", + "description": "Use the local in-memory cache based implementation as the lock provider." + }, + { + "value": "redis", + "description": "Use the redis-lock implementation as the lock provider." + }, + { + "value": "zookeeper", + "description": "Use the zookeeper-lock implementation as the lock provider." + } + ] + } + ] +} diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java new file mode 100644 index 000000000..65372ac86 --- /dev/null +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java @@ -0,0 +1,62 @@ +/* + *

+ * 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 com.netflix.conductor.contribs.listener; + +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.contribs.listener.archive.ArchivingWorkflowStatusListener; +import com.netflix.conductor.core.dal.ExecutionDAOFacade; +import com.netflix.conductor.model.WorkflowModel; + +import static org.mockito.Mockito.*; + +/** + * @author pavel.halabala + */ +public class ArchivingWorkflowStatusListenerTest { + + WorkflowModel workflow; + ExecutionDAOFacade executionDAOFacade; + ArchivingWorkflowStatusListener listener; + + @Before + public void before() { + workflow = new WorkflowModel(); + WorkflowDef def = new WorkflowDef(); + def.setName("name1"); + def.setVersion(1); + workflow.setWorkflowDefinition(def); + workflow.setWorkflowId(UUID.randomUUID().toString()); + + executionDAOFacade = Mockito.mock(ExecutionDAOFacade.class); + listener = new ArchivingWorkflowStatusListener(executionDAOFacade); + } + + @Test + public void testArchiveOnWorkflowCompleted() { + listener.onWorkflowCompleted(workflow); + verify(executionDAOFacade, times(1)).removeWorkflow(workflow.getWorkflowId(), true); + verifyNoMoreInteractions(executionDAOFacade); + } + + @Test + public void testArchiveOnWorkflowTerminated() { + listener.onWorkflowTerminated(workflow); + verify(executionDAOFacade, times(1)).removeWorkflow(workflow.getWorkflowId(), true); + verifyNoMoreInteractions(executionDAOFacade); + } +} diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java new file mode 100644 index 000000000..86fc0017c --- /dev/null +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java @@ -0,0 +1,214 @@ +/* + *

+ * 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 com.netflix.conductor.test.listener; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.dao.QueueDAO; +import com.netflix.conductor.service.ExecutionService; +import com.netflix.conductor.service.MetadataService; +import com.netflix.conductor.service.WorkflowService; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.netflix.conductor.common.metadata.tasks.Task.Status.COMPLETED; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@SpringBootTest( + properties = { + "conductor.workflow-status-listener.type=queue_publisher", + "conductor.workflow-status-listener.queue-publisher.successQueue=dummy", + "conductor.workflow-status-listener.queue-publisher.failureQueue=dummy", + "conductor.workflow-status-listener.queue-publisher.finalizeQueue=final" + }) +@TestPropertySource(locations = "classpath:application-integrationtest.properties") +public class WorkflowStatusPublisherIntegrationTest { + + private final String CALLBACK_QUEUE = "dummy"; + private final String FINALIZED_QUEUE = "final"; + private static final String LINEAR_WORKFLOW_T1_T2 = "junit_test_wf"; + private static final int WORKFLOW_VERSION = 1; + private static final String INCOMPLETION_REASON = "test reason"; + private static final String DEFAULT_OWNER_EMAIL = "test@harness.com"; + + @Autowired private ObjectMapper objectMapper; + + @Autowired QueueDAO queueDAO; + + @Autowired protected MetadataService metadataService; + + @Autowired protected ExecutionService workflowExecutionService; + + @Autowired protected WorkflowService workflowExecutor; + + @Before + public void setUp() { + TaskDef taskDef = new TaskDef(); + taskDef.setName("junit_task_1"); + taskDef.setTimeoutSeconds(120); + taskDef.setResponseTimeoutSeconds(120); + taskDef.setRetryCount(1); + taskDef.setOwnerEmail(DEFAULT_OWNER_EMAIL); + metadataService.registerTaskDef(Collections.singletonList(taskDef)); + } + + @After + public void cleanUp() { + List workflows = + metadataService.getWorkflowDefs().stream() + .map(WorkflowDef::getName) + .collect(Collectors.toList()); + for (String wfName : workflows) { + List running = + workflowExecutionService.getRunningWorkflows(wfName, WORKFLOW_VERSION); + for (String wfid : running) { + workflowExecutor.terminateWorkflow(wfid, "cleanup"); + } + } + queueDAO.queuesDetail().keySet().forEach(queueDAO::flush); + } + + @Test + public void testListenerOnTerminatedWorkflow() throws IOException { + String id = + startOrLoadWorkflowExecution( + LINEAR_WORKFLOW_T1_T2, + 1, + "testWorkflowTerminatedListener", + new HashMap<>()); + workflowExecutor.terminateWorkflow(id, INCOMPLETION_REASON); + + List callbackMessages = queueDAO.pollMessages(CALLBACK_QUEUE, 1, 200); + queueDAO.ack(CALLBACK_QUEUE, callbackMessages.get(0).getId()); + + WorkflowSummary payload = + objectMapper.readValue(callbackMessages.get(0).getPayload(), WorkflowSummary.class); + assertEquals(id, callbackMessages.get(0).getId()); + assertEquals(LINEAR_WORKFLOW_T1_T2, payload.getWorkflowType()); + assertEquals("testWorkflowTerminatedListener", payload.getCorrelationId()); + assertEquals(Workflow.WorkflowStatus.TERMINATED, payload.getStatus()); + assertEquals(INCOMPLETION_REASON, payload.getReasonForIncompletion()); + + // check finalized queue + callbackMessages = queueDAO.pollMessages(FINALIZED_QUEUE, 1, 200); + queueDAO.ack(CALLBACK_QUEUE, callbackMessages.get(0).getId()); + + payload = + objectMapper.readValue(callbackMessages.get(0).getPayload(), WorkflowSummary.class); + assertEquals(id, callbackMessages.get(0).getId()); + assertEquals(LINEAR_WORKFLOW_T1_T2, payload.getWorkflowType()); + assertEquals("testWorkflowTerminatedListener", payload.getCorrelationId()); + assertEquals(Workflow.WorkflowStatus.TERMINATED, payload.getStatus()); + assertEquals(INCOMPLETION_REASON, payload.getReasonForIncompletion()); + } + + @Test + public void testListenerOnCompletedWorkflow() throws IOException, InterruptedException { + WorkflowDef workflowDef = new WorkflowDef(); + workflowDef.setName(LINEAR_WORKFLOW_T1_T2); + workflowDef.setDescription(workflowDef.getName()); + workflowDef.setVersion(WORKFLOW_VERSION); + workflowDef.setSchemaVersion(2); + workflowDef.setOwnerEmail(DEFAULT_OWNER_EMAIL); + workflowDef.setWorkflowStatusListenerEnabled(true); + LinkedList wftasks = new LinkedList<>(); + + WorkflowTask wft1 = new WorkflowTask(); + wft1.setName("junit_task_1"); + wft1.setTaskReferenceName("t1"); + + wftasks.add(wft1); + workflowDef.setTasks(wftasks); + + metadataService.updateWorkflowDef(Collections.singletonList(workflowDef)); + + String id = + startOrLoadWorkflowExecution( + workflowDef.getName(), 1, "testWorkflowCompletedListener", new HashMap<>()); + + List tasks = workflowExecutionService.getTasks("junit_task_1", null, 1); + tasks.get(0).setStatus(COMPLETED); + workflowExecutionService.updateTask(new TaskResult(tasks.get(0))); + + checkIfWorkflowIsCompleted(id); + + List callbackMessages = queueDAO.pollMessages(CALLBACK_QUEUE, 1, 200); + queueDAO.ack(CALLBACK_QUEUE, callbackMessages.get(0).getId()); + + WorkflowSummary payload = + objectMapper.readValue(callbackMessages.get(0).getPayload(), WorkflowSummary.class); + assertEquals(id, callbackMessages.get(0).getId()); + assertEquals(LINEAR_WORKFLOW_T1_T2, payload.getWorkflowType()); + assertEquals("testWorkflowCompletedListener", payload.getCorrelationId()); + assertEquals(Workflow.WorkflowStatus.COMPLETED, payload.getStatus()); + + // check finalized queue + callbackMessages = queueDAO.pollMessages(FINALIZED_QUEUE, 1, 200); + queueDAO.ack(CALLBACK_QUEUE, callbackMessages.get(0).getId()); + + payload = + objectMapper.readValue(callbackMessages.get(0).getPayload(), WorkflowSummary.class); + assertEquals(id, callbackMessages.get(0).getId()); + assertEquals(LINEAR_WORKFLOW_T1_T2, payload.getWorkflowType()); + assertEquals("testWorkflowCompletedListener", payload.getCorrelationId()); + assertEquals(Workflow.WorkflowStatus.COMPLETED, payload.getStatus()); + } + + @SuppressWarnings("BusyWait") + private void checkIfWorkflowIsCompleted(String id) throws InterruptedException { + int statusRetrieveAttempts = 0; + while (workflowExecutor.getExecutionStatus(id, false).getStatus() + != Workflow.WorkflowStatus.COMPLETED) { + if (statusRetrieveAttempts > 5) { + break; + } + Thread.sleep(100); + statusRetrieveAttempts++; + } + } + + private String startOrLoadWorkflowExecution( + String workflowName, int version, String correlationId, Map input) { + StartWorkflowRequest startWorkflowInput = new StartWorkflowRequest(); + startWorkflowInput.setName(workflowName); + startWorkflowInput.setVersion(version); + startWorkflowInput.setCorrelationId(correlationId); + startWorkflowInput.setInput(input); + return workflowExecutor.startWorkflow(startWorkflowInput); + } +} diff --git a/workflow-event-listener/src/test/resources/application-integrationtest.properties b/workflow-event-listener/src/test/resources/application-integrationtest.properties new file mode 100644 index 000000000..7e307590f --- /dev/null +++ b/workflow-event-listener/src/test/resources/application-integrationtest.properties @@ -0,0 +1,55 @@ +# +# /* +# * Copyright 2021 Netflix, Inc. +# *

+# * 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. +# */ +# + +conductor.db.type=memory +conductor.workflow-execution-lock.type=local_only +conductor.external-payload-storage.type=dummy +conductor.indexing.enabled=false + +conductor.app.stack=test +conductor.app.appId=conductor + +conductor.app.workflow-offset-timeout=30s + +conductor.system-task-workers.enabled=false +conductor.app.system-task-worker-callback-duration=0 + +conductor.app.event-message-indexing-enabled=true +conductor.app.event-execution-indexing-enabled=true + +conductor.workflow-reconciler.enabled=true +conductor.workflow-repair-service.enabled=false + +conductor.app.workflow-execution-lock-enabled=false + +conductor.app.workflow-input-payload-size-threshold=10KB +conductor.app.max-workflow-input-payload-size-threshold=10240KB +conductor.app.workflow-output-payload-size-threshold=10KB +conductor.app.max-workflow-output-payload-size-threshold=10240KB +conductor.app.task-input-payload-size-threshold=10KB +conductor.app.max-task-input-payload-size-threshold=10240KB +conductor.app.task-output-payload-size-threshold=10KB +conductor.app.max-task-output-payload-size-threshold=10240KB +conductor.app.max-workflow-variables-payload-size-threshold=2KB + +conductor.redis.availability-zone=us-east-1c +conductor.redis.data-center-region=us-east-1 +conductor.redis.workflow-namespace-prefix=integration-test +conductor.redis.queue-namespace-prefix=integtest + +conductor.elasticsearch.index-prefix=conductor +conductor.elasticsearch.cluster-health-color=yellow + +management.metrics.export.datadog.enabled=false From 31505dd956290a405b0ec7972ce013983cfd7981 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 12:53:21 -0800 Subject: [PATCH 033/202] kafka task --- dependencies.gradle | 1 + kafka/README.md | 182 ++++ kafka/build.gradle | 30 + kafka/dependencies.lock | 990 ++++++++++++++++++ .../tasks/kafka/KafkaProducerManager.java | 111 ++ .../tasks/kafka/KafkaPublishTask.java | 313 ++++++ .../mapper/KafkaPublishTaskMapper.java | 97 ++ .../integration/KafkaPublishTaskSpec.groovy | 176 ++++ .../tasks/kafka/KafkaProducerManagerTest.java | 135 +++ .../tasks/kafka/KafkaPublishTaskTest.java | 223 ++++ .../mapper/KafkaPublishTaskMapperTest.java | 122 +++ .../application-integrationtest.properties | 55 + kafka/src/test/resources/input.json | 0 kafka/src/test/resources/output.json | 424 ++++++++ ...le_json_jq_transform_integration_test.json | 32 + settings.gradle | 1 + 16 files changed, 2892 insertions(+) create mode 100644 kafka/README.md create mode 100644 kafka/build.gradle create mode 100644 kafka/dependencies.lock create mode 100644 kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java create mode 100644 kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java create mode 100644 kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java create mode 100644 kafka/src/test/groovy/com/netflix/conductor/test/integration/KafkaPublishTaskSpec.groovy create mode 100644 kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java create mode 100644 kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java create mode 100644 kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java create mode 100644 kafka/src/test/resources/application-integrationtest.properties create mode 100644 kafka/src/test/resources/input.json create mode 100644 kafka/src/test/resources/output.json create mode 100644 kafka/src/test/resources/simple_json_jq_transform_integration_test.json diff --git a/dependencies.gradle b/dependencies.gradle index 7e3d65dce..63c90e0be 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -58,5 +58,6 @@ ext { revTestContainer = '1.15.3' revFasterXml = '2.15.3' revAmqpClient = '5.13.0' + revKafka = '2.6.0' } diff --git a/kafka/README.md b/kafka/README.md new file mode 100644 index 000000000..bb683cf39 --- /dev/null +++ b/kafka/README.md @@ -0,0 +1,182 @@ +# Community contributed system tasks + +## Published Artifacts + +Group: `com.netflix.conductor` + +| Published Artifact | Description | +| ----------- | ----------- | +| conductor-task | Community contributed tasks | + +**Note**: If you are using `condutor-contribs` as a dependency, the task module is already included, you do not need to include it separately. + +## JsonJQTransform +JSON_JQ_TRANSFORM_TASK is a System task that allows processing of JSON data that is supplied to the task, by using the +popular JQ processing tool’s query expression language. + + +```json +"type" : "JSON_JQ_TRANSFORM_TASK" +``` +Check the [JQ Manual](https://stedolan.github.io/jq/manual/v1.5/), and the +[JQ Playground](https://jqplay.org/) for more information on JQ + +### Use Cases + +JSON is a popular format of choice for data-interchange. It is widely used in web and server applications, document +storage, API I/O etc. It’s also used within Conductor to define workflow and task definitions and passing data and state +between tasks and workflows. This makes a tool like JQ a natural fit for processing task related data. Some common +usages within Conductor includes, working with HTTP task, JOIN tasks or standalone tasks that try to transform data from +the output of one task to the input of another. + +### Configuration + +Here is an example of a _`JSON_JQ_TRANSFORM`_ task. The `inputParameters` attribute is expected to have a value object +that has the following + +1. A list of key value pair objects denoted key1/value1, key2/value2 in the example below. Note the key1/value1 are + arbitrary names used in this example. + +2. A key with the name `queryExpression`, whose value is a JQ expression. The expression will operate on the value of + the `inputParameters` attribute. In the example below, the `inputParameters` has 2 inner objects named by attributes + `key1` and `key2`, each of which has an object that is named `value1` and `value2`. They have an associated array of + strings as values, `"a", "b"` and `"c", "d"`. The expression `key3: (.key1.value1 + .key2.value2)` concats the 2 + string arrays into a single array against an attribute named `key3` + +```json +{ + "name": "jq_example_task", + "taskReferenceName": "my_jq_example_task", + "type": "JSON_JQ_TRANSFORM", + "inputParameters": { + "key1": { + "value1": [ + "a", + "b" + ] + }, + "key2": { + "value2": [ + "c", + "d" + ] + }, + "queryExpression": "{ key3: (.key1.value1 + .key2.value2) }" + } +} +``` + +The execution of this example task above will provide the following output. The `resultList` attribute stores the full +list of the `queryExpression` result. The `result` attribute stores the first element of the resultList. An +optional `error` +attribute along with a string message will be returned if there was an error processing the query expression. + +```json +{ + "result": { + "key3": [ + "a", + "b", + "c", + "d" + ] + }, + "resultList": [ + { + "key3": [ + "a", + "b", + "c", + "d" + ] + } + ] +} +``` + +#### Input Configuration + +| Attribute | Description | +| ----------- | ----------- | +| name | Task Name. A unique name that is descriptive of the task function | +| taskReferenceName | Task Reference Name. A unique reference to this task. There can be multiple references of a task within the same workflow definition | +| type | Task Type. In this case, JSON_JQ_TRANSFORM | +| inputParameters | The input parameters that will be supplied to this task. The parameters will be a JSON object of atleast 2 attributes, one of which will be called queryExpression. The others are user named attributes. These attributes will be accessible by the JQ query processor | +| inputParameters/user-defined-key(s) | User defined key(s) along with values. | +| inputParameters/queryExpression | A JQ query expression | + +#### Output Configuration + +| Attribute | Description | +| ----------- | ----------- | +| result | The first results returned by the JQ expression | +| resultList | A List of results returned by the JQ expression | +| error | An optional error message, indicating that the JQ query failed processing | + + +## Kafka Publish Task +A Kafka Publish task is used to push messages to another microservice via Kafka. + +```json +"type" : "KAFKA_PUBLISH" +``` + +### Examples + +Sample Task + +```json +{ + "name": "call_kafka", + "taskReferenceName": "call_kafka", + "inputParameters": { + "kafka_request": { + "topic": "userTopic", + "value": "Message to publish", + "bootStrapServers": "localhost:9092", + "headers": { + "x-Auth":"Auth-key" + }, + "key": "123", + "keySerializer": "org.apache.kafka.common.serialization.IntegerSerializer" + } + }, + "type": "KAFKA_PUBLISH" +} +``` + +The task expects an input parameter named `"kafka_request"` as part +of the task's input with the following details: + +1. `"bootStrapServers"` - bootStrapServers for connecting to given kafka. +2. `"key"` - Key to be published. +3. `"keySerializer"` - Serializer used for serializing the key published to kafka. + One of the following can be set : + a. org.apache.kafka.common.serialization.IntegerSerializer + b. org.apache.kafka.common.serialization.LongSerializer + c. org.apache.kafka.common.serialization.StringSerializer. + Default is String serializer. +4. `"value"` - Value published to kafka +5. `"requestTimeoutMs"` - Request timeout while publishing to kafka. + If this value is not given the value is read from the property + kafka.publish.request.timeout.ms. If the property is not set the value + defaults to 100 ms. +6. `"maxBlockMs"` - maxBlockMs while publishing to kafka. If this value is + not given the value is read from the property kafka.publish.max.block.ms. + If the property is not set the value defaults to 500 ms. +7. `"headers"` - A map of additional kafka headers to be sent along with + the request. +8. `"topic"` - Topic to publish. + +The producer created in the kafka task is cached. By default +the cache size is 10 and expiry time is 120000 ms. To change the +defaults following can be modified +kafka.publish.producer.cache.size, +kafka.publish.producer.cache.time.ms respectively. + +#### Kafka Task Output + +Task status transitions to `COMPLETED`. + +The task is marked as `FAILED` if the message could not be published to +the Kafka queue. \ No newline at end of file diff --git a/kafka/build.gradle b/kafka/build.gradle new file mode 100644 index 000000000..13339d752 --- /dev/null +++ b/kafka/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'groovy' + +dependencies { + + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-test' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + compileOnly "org.springframework:spring-web" + + implementation "org.apache.commons:commons-lang3:" + implementation "com.google.guava:guava:${revGuava}" + implementation "javax.ws.rs:jsr311-api:${revJsr311Api}" + implementation "io.reactivex:rxjava:${revRxJava}" + implementation "org.apache.kafka:kafka-clients:${revKafka}" + + testImplementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation "org.testcontainers:mockserver:${revTestContainer}" + testImplementation "org.mock-server:mockserver-client-java:${revMockServerClient}" + + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" + testImplementation "org.spockframework:spock-core:${revSpock}" + testImplementation "org.spockframework:spock-spring:${revSpock}" + + testImplementation project(':conductor-test-util') + testImplementation project(':conductor-test-util').sourceSets.test.output + +} diff --git a/kafka/dependencies.lock b/kafka/dependencies.lock new file mode 100644 index 000000000..080a6e521 --- /dev/null +++ b/kafka/dependencies.lock @@ -0,0 +1,990 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "3.1.4" + } + }, + "compileClasspath": { + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.kafka:kafka-clients": { + "locked": "2.6.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-test": { + "locked": "3.1.4" + }, + "org.springframework:spring-web": { + "locked": "6.0.12" + } + }, + "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.12.0" + }, + "org.apache.kafka:kafka-clients": { + "locked": "2.6.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + } + }, + "testCompileClasspath": { + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-test-util": { + "project": true + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.kafka:kafka-clients": { + "locked": "2.6.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.mock-server:mockserver-client-java": { + "locked": "5.12.0" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "3.1.4" + }, + "org.testcontainers:mockserver": { + "locked": "1.15.3" + } + }, + "testRuntimeClasspath": { + "com.amazonaws:aws-java-sdk-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "3.22.3" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "project": true + }, + "com.netflix.conductor:conductor-test-util": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.0.20" + }, + "com.netflix.eureka:eureka-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.10.10" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "locked": "5.17.1" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.sun.jersey:jersey-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.19.4" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.11.0" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:javax.ws.rs-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.1.1" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.kafka:kafka-clients": { + "locked": "2.6.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "8.7.1" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.12" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.glassfish.jersey.core:jersey-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "3.1.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.mock-server:mockserver-client-java": { + "locked": "5.12.0" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.slf4j:slf4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.0.9" + }, + "org.spockframework:spock-core": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.spockframework:spock-spring": { + "locked": "2.4-M1-groovy-4.0" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "org.testcontainers:elasticsearch": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "locked": "1.18.3" + }, + "org.testcontainers:mockserver": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "locked": "1.18.3" + }, + "org.testcontainers:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-test-util" + ], + "locked": "1.18.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-test-util" + ], + "locked": "3.3.0" + } + } +} \ No newline at end of file diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java new file mode 100644 index 000000000..af6100b2a --- /dev/null +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.tasks.kafka; + +import java.time.Duration; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; + +@SuppressWarnings("rawtypes") +@Component +public class KafkaProducerManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaProducerManager.class); + + private final String requestTimeoutConfig; + private final Cache kafkaProducerCache; + private final String maxBlockMsConfig; + + private static final String STRING_SERIALIZER = + "org.apache.kafka.common.serialization.StringSerializer"; + private static final RemovalListener LISTENER = + notification -> { + if (notification.getValue() != null) { + notification.getValue().close(); + LOGGER.info("Closed producer for {}", notification.getKey()); + } + }; + + @Autowired + public KafkaProducerManager( + @Value("${conductor.tasks.kafka-publish.requestTimeout:100ms}") Duration requestTimeout, + @Value("${conductor.tasks.kafka-publish.maxBlock:500ms}") Duration maxBlock, + @Value("${conductor.tasks.kafka-publish.cacheSize:10}") int cacheSize, + @Value("${conductor.tasks.kafka-publish.cacheTime:120000ms}") Duration cacheTime) { + this.requestTimeoutConfig = String.valueOf(requestTimeout.toMillis()); + this.maxBlockMsConfig = String.valueOf(maxBlock.toMillis()); + this.kafkaProducerCache = + CacheBuilder.newBuilder() + .removalListener(LISTENER) + .maximumSize(cacheSize) + .expireAfterAccess(cacheTime.toMillis(), TimeUnit.MILLISECONDS) + .build(); + } + + public Producer getProducer(KafkaPublishTask.Input input) { + Properties configProperties = getProducerProperties(input); + return getFromCache(configProperties, () -> new KafkaProducer(configProperties)); + } + + @VisibleForTesting + Producer getFromCache(Properties configProperties, Callable createProducerCallable) { + try { + return kafkaProducerCache.get(configProperties, createProducerCallable); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + @VisibleForTesting + Properties getProducerProperties(KafkaPublishTask.Input input) { + + Properties configProperties = new Properties(); + configProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, input.getBootStrapServers()); + + configProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, input.getKeySerializer()); + + String requestTimeoutMs = requestTimeoutConfig; + + if (Objects.nonNull(input.getRequestTimeoutMs())) { + requestTimeoutMs = String.valueOf(input.getRequestTimeoutMs()); + } + + String maxBlockMs = maxBlockMsConfig; + + if (Objects.nonNull(input.getMaxBlockMs())) { + maxBlockMs = String.valueOf(input.getMaxBlockMs()); + } + + configProperties.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, requestTimeoutMs); + configProperties.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, maxBlockMs); + configProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, STRING_SERIALIZER); + return configProperties; + } +} diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java new file mode 100644 index 000000000..870a8903a --- /dev/null +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java @@ -0,0 +1,313 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.tasks.kafka; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.header.Header; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.apache.kafka.common.serialization.IntegerSerializer; +import org.apache.kafka.common.serialization.LongSerializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.netflix.conductor.core.execution.WorkflowExecutor; +import com.netflix.conductor.core.execution.tasks.WorkflowSystemTask; +import com.netflix.conductor.core.utils.Utils; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; + +import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_KAFKA_PUBLISH; + +@Component(TASK_TYPE_KAFKA_PUBLISH) +public class KafkaPublishTask extends WorkflowSystemTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaPublishTask.class); + + static final String REQUEST_PARAMETER_NAME = "kafka_request"; + private static final String MISSING_REQUEST = + "Missing Kafka request. Task input MUST have a '" + + REQUEST_PARAMETER_NAME + + "' key with KafkaTask.Input as value. See documentation for KafkaTask for required input parameters"; + private static final String MISSING_BOOT_STRAP_SERVERS = "No boot strap servers specified"; + private static final String MISSING_KAFKA_TOPIC = + "Missing Kafka topic. See documentation for KafkaTask for required input parameters"; + private static final String MISSING_KAFKA_VALUE = + "Missing Kafka value. See documentation for KafkaTask for required input parameters"; + private static final String FAILED_TO_INVOKE = "Failed to invoke kafka task due to: "; + + private final ObjectMapper objectMapper; + private final String requestParameter; + private final KafkaProducerManager producerManager; + + @Autowired + public KafkaPublishTask(KafkaProducerManager clientManager, ObjectMapper objectMapper) { + super(TASK_TYPE_KAFKA_PUBLISH); + this.requestParameter = REQUEST_PARAMETER_NAME; + this.producerManager = clientManager; + this.objectMapper = objectMapper; + LOGGER.info("KafkaTask initialized."); + } + + @Override + public void start(WorkflowModel workflow, TaskModel task, WorkflowExecutor executor) { + + long taskStartMillis = Instant.now().toEpochMilli(); + task.setWorkerId(Utils.getServerId()); + Object request = task.getInputData().get(requestParameter); + + if (Objects.isNull(request)) { + markTaskAsFailed(task, MISSING_REQUEST); + return; + } + + Input input = objectMapper.convertValue(request, Input.class); + + if (StringUtils.isBlank(input.getBootStrapServers())) { + markTaskAsFailed(task, MISSING_BOOT_STRAP_SERVERS); + return; + } + + if (StringUtils.isBlank(input.getTopic())) { + markTaskAsFailed(task, MISSING_KAFKA_TOPIC); + return; + } + + if (Objects.isNull(input.getValue())) { + markTaskAsFailed(task, MISSING_KAFKA_VALUE); + return; + } + + try { + Future recordMetaDataFuture = kafkaPublish(input); + try { + recordMetaDataFuture.get(); + if (isAsyncComplete(task)) { + task.setStatus(TaskModel.Status.IN_PROGRESS); + } else { + task.setStatus(TaskModel.Status.COMPLETED); + } + long timeTakenToCompleteTask = Instant.now().toEpochMilli() - taskStartMillis; + LOGGER.debug("Published message {}, Time taken {}", input, timeTakenToCompleteTask); + + } catch (ExecutionException ec) { + LOGGER.error( + "Failed to invoke kafka task: {} - execution exception ", + task.getTaskId(), + ec); + markTaskAsFailed(task, FAILED_TO_INVOKE + ec.getMessage()); + } + } catch (Exception e) { + LOGGER.error( + "Failed to invoke kafka task:{} for input {} - unknown exception", + task.getTaskId(), + input, + e); + markTaskAsFailed(task, FAILED_TO_INVOKE + e.getMessage()); + } + } + + private void markTaskAsFailed(TaskModel task, String reasonForIncompletion) { + task.setReasonForIncompletion(reasonForIncompletion); + task.setStatus(TaskModel.Status.FAILED); + } + + /** + * @param input Kafka Request + * @return Future for execution. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private Future kafkaPublish(Input input) throws Exception { + + long startPublishingEpochMillis = Instant.now().toEpochMilli(); + + Producer producer = producerManager.getProducer(input); + + long timeTakenToCreateProducer = Instant.now().toEpochMilli() - startPublishingEpochMillis; + + LOGGER.debug("Time taken getting producer {}", timeTakenToCreateProducer); + + Object key = getKey(input); + + Iterable

headers = + input.getHeaders().entrySet().stream() + .map( + header -> + new RecordHeader( + header.getKey(), + String.valueOf(header.getValue()).getBytes())) + .collect(Collectors.toList()); + ProducerRecord rec = + new ProducerRecord( + input.getTopic(), + null, + null, + key, + objectMapper.writeValueAsString(input.getValue()), + headers); + + Future send = producer.send(rec); + + long timeTakenToPublish = Instant.now().toEpochMilli() - startPublishingEpochMillis; + + LOGGER.debug("Time taken publishing {}", timeTakenToPublish); + + return send; + } + + @VisibleForTesting + Object getKey(Input input) { + String keySerializer = input.getKeySerializer(); + + if (LongSerializer.class.getCanonicalName().equals(keySerializer)) { + return Long.parseLong(String.valueOf(input.getKey())); + } else if (IntegerSerializer.class.getCanonicalName().equals(keySerializer)) { + return Integer.parseInt(String.valueOf(input.getKey())); + } else { + return String.valueOf(input.getKey()); + } + } + + @Override + public boolean execute(WorkflowModel workflow, TaskModel task, WorkflowExecutor executor) { + return false; + } + + @Override + public void cancel(WorkflowModel workflow, TaskModel task, WorkflowExecutor executor) { + task.setStatus(TaskModel.Status.CANCELED); + } + + @Override + public boolean isAsync() { + return true; + } + + public static class Input { + + public static final String STRING_SERIALIZER = StringSerializer.class.getCanonicalName(); + private Map headers = new HashMap<>(); + private String bootStrapServers; + private Object key; + private Object value; + private Integer requestTimeoutMs; + private Integer maxBlockMs; + private String topic; + private String keySerializer = STRING_SERIALIZER; + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public String getBootStrapServers() { + return bootStrapServers; + } + + public void setBootStrapServers(String bootStrapServers) { + this.bootStrapServers = bootStrapServers; + } + + public Object getKey() { + return key; + } + + public void setKey(Object key) { + this.key = key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public Integer getRequestTimeoutMs() { + return requestTimeoutMs; + } + + public void setRequestTimeoutMs(Integer requestTimeoutMs) { + this.requestTimeoutMs = requestTimeoutMs; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public String getKeySerializer() { + return keySerializer; + } + + public void setKeySerializer(String keySerializer) { + this.keySerializer = keySerializer; + } + + public Integer getMaxBlockMs() { + return maxBlockMs; + } + + public void setMaxBlockMs(Integer maxBlockMs) { + this.maxBlockMs = maxBlockMs; + } + + @Override + public String toString() { + return "Input{" + + "headers=" + + headers + + ", bootStrapServers='" + + bootStrapServers + + '\'' + + ", key=" + + key + + ", value=" + + value + + ", requestTimeoutMs=" + + requestTimeoutMs + + ", maxBlockMs=" + + maxBlockMs + + ", topic='" + + topic + + '\'' + + ", keySerializer='" + + keySerializer + + '\'' + + '}'; + } + } +} diff --git a/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java new file mode 100644 index 000000000..98bfacb5f --- /dev/null +++ b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java @@ -0,0 +1,97 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.core.execution.mapper; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.core.exception.TerminateWorkflowException; +import com.netflix.conductor.core.utils.ParametersUtils; +import com.netflix.conductor.dao.MetadataDAO; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +@Component +public class KafkaPublishTaskMapper implements TaskMapper { + + public static final Logger LOGGER = LoggerFactory.getLogger(KafkaPublishTaskMapper.class); + + private final ParametersUtils parametersUtils; + private final MetadataDAO metadataDAO; + + @Autowired + public KafkaPublishTaskMapper(ParametersUtils parametersUtils, MetadataDAO metadataDAO) { + this.parametersUtils = parametersUtils; + this.metadataDAO = metadataDAO; + } + + @Override + public String getTaskType() { + return TaskType.KAFKA_PUBLISH.name(); + } + + /** + * This method maps a {@link WorkflowTask} of type {@link TaskType#KAFKA_PUBLISH} to a {@link + * TaskModel} in a {@link TaskModel.Status#SCHEDULED} state + * + * @param taskMapperContext: A wrapper class containing the {@link WorkflowTask}, {@link + * WorkflowDef}, {@link WorkflowModel} and a string representation of the TaskId + * @return a List with just one Kafka task + * @throws TerminateWorkflowException In case if the task definition does not exist + */ + @Override + public List getMappedTasks(TaskMapperContext taskMapperContext) + throws TerminateWorkflowException { + + LOGGER.debug("TaskMapperContext {} in KafkaPublishTaskMapper", taskMapperContext); + + WorkflowTask workflowTask = taskMapperContext.getWorkflowTask(); + WorkflowModel workflowModel = taskMapperContext.getWorkflowModel(); + String taskId = taskMapperContext.getTaskId(); + int retryCount = taskMapperContext.getRetryCount(); + + TaskDef taskDefinition = + Optional.ofNullable(taskMapperContext.getTaskDefinition()) + .orElseGet(() -> metadataDAO.getTaskDef(workflowTask.getName())); + + Map input = + parametersUtils.getTaskInputV2( + workflowTask.getInputParameters(), workflowModel, taskId, taskDefinition); + + TaskModel kafkaPublishTask = taskMapperContext.createTaskModel(); + kafkaPublishTask.setInputData(input); + kafkaPublishTask.setStatus(TaskModel.Status.SCHEDULED); + kafkaPublishTask.setRetryCount(retryCount); + kafkaPublishTask.setCallbackAfterSeconds(workflowTask.getStartDelay()); + if (Objects.nonNull(taskDefinition)) { + kafkaPublishTask.setExecutionNameSpace(taskDefinition.getExecutionNameSpace()); + kafkaPublishTask.setIsolationGroupId(taskDefinition.getIsolationGroupId()); + kafkaPublishTask.setRateLimitPerFrequency(taskDefinition.getRateLimitPerFrequency()); + kafkaPublishTask.setRateLimitFrequencyInSeconds( + taskDefinition.getRateLimitFrequencyInSeconds()); + } + return Collections.singletonList(kafkaPublishTask); + } +} diff --git a/kafka/src/test/groovy/com/netflix/conductor/test/integration/KafkaPublishTaskSpec.groovy b/kafka/src/test/groovy/com/netflix/conductor/test/integration/KafkaPublishTaskSpec.groovy new file mode 100644 index 000000000..8b4050a73 --- /dev/null +++ b/kafka/src/test/groovy/com/netflix/conductor/test/integration/KafkaPublishTaskSpec.groovy @@ -0,0 +1,176 @@ +/* + * Copyright 2022 Netflix, Inc. + *

+ * 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 com.netflix.conductor.test.integration + +import com.fasterxml.jackson.databind.ObjectMapper +import com.netflix.conductor.common.metadata.tasks.TaskDef +import com.netflix.conductor.common.metadata.tasks.TaskResult +import com.netflix.conductor.common.metadata.tasks.TaskType +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest +import com.netflix.conductor.common.metadata.workflow.WorkflowDef +import com.netflix.conductor.common.metadata.workflow.WorkflowTask +import com.netflix.conductor.common.run.Workflow +import com.netflix.conductor.core.execution.StartWorkflowInput +import com.netflix.conductor.test.base.AbstractSpecification +import org.springframework.beans.factory.annotation.Autowired +import spock.lang.Shared + +class KafkaPublishTaskSpec extends AbstractSpecification { + + @Autowired + ObjectMapper objectMapper + + @Shared + def isWorkflowRegistered = false + + def kafkaInput = ['requestDetails': ['key1': 'value1', 'key2': 42], + 'path1' : 'file://path1', + 'path2' : 'file://path2', + 'outputPath' : 's3://bucket/outputPath' + ] + + def expectedTaskInput = "{\"kafka_request\":{\"topic\":\"test_kafka_topic\",\"bootStrapServers\":\"localhost:9092\",\"value\":{\"requestDetails\":{\"key1\":\"value1\",\"key2\":42},\"outputPath\":\"s3://bucket/outputPath\",\"inputPaths\":[\"file://path1\",\"file://path2\"]}}}" + + def setup() { + if (!isWorkflowRegistered) { + registerKafkaWorkflow() + isWorkflowRegistered = true + } + } + + def "Test the kafka template usage failure case"() { + + given: "Start a workflow based on the registered workflow" + def workflowInstanceId = workflowService.startWorkflow("template_kafka_workflow", 1, + "testTaskDefTemplate", 0, kafkaInput) + + and: "Get the workflow based on the Id that is being executed" + def workflow = workflowExecutionService.getExecutionStatus(workflowInstanceId, true) + def task = workflow.tasks.get(0) + def taskInput = task.inputData + + when: "Ensure that the task is pollable and fail the task" + def polledTask = workflowExecutionService.poll('KAFKA_PUBLISH', 'test') + workflowExecutionService.ackTaskReceived(polledTask.taskId) + def taskResult = new TaskResult(polledTask) + taskResult.status = TaskResult.Status.FAILED + taskResult.reasonForIncompletion = 'NON TRANSIENT ERROR OCCURRED: An integration point required to complete the task is down' + taskResult.addOutputData("TERMINAL_ERROR", "Integration endpoint down: FOOBAR") + taskResult.addOutputData("ErrorMessage", "There was a terminal error") + workflowExecutionService.updateTask(taskResult) + + and: "Then run a decide to move the workflow forward" + workflowExecutor.decide(workflowInstanceId) + + and: "Get the updated workflow after the task result has been updated" + def updatedWorkflow = workflowExecutionService.getExecutionStatus(workflowInstanceId, true) + + then: "Check that the workflow is created and is not terminal" + workflowInstanceId + workflow + !workflow.getStatus().isTerminal() + !workflow.getReasonForIncompletion() + + and: "Check if the input of the next task to be polled is as expected for a kafka task" + taskInput + taskInput.containsKey('kafka_request') + taskInput['kafka_request'] instanceof Map + objectMapper.writeValueAsString(taskInput) == expectedTaskInput + + and: "Polled task is not null and the workflowInstanceId of the task is same as the workflow created initially" + polledTask + polledTask.workflowInstanceId == workflowInstanceId + + and: "The updated workflow is in a failed state" + updatedWorkflow + updatedWorkflow.status == Workflow.WorkflowStatus.FAILED + } + + def "Test the kafka template usage success case"() { + + given: "Start a workflow based on the registered kafka workflow" + def workflowInstanceId = workflowService.startWorkflow("template_kafka_workflow", 1, + "testTaskDefTemplate", 0, kafkaInput) + + and: "Get the workflow based on the Id that is being executed" + def workflow = workflowExecutionService.getExecutionStatus(workflowInstanceId, true) + def task = workflow.tasks.get(0) + def taskInput = task.inputData + + when: "Ensure that the task is pollable and complete the task" + def polledTask = workflowExecutionService.poll('KAFKA_PUBLISH', 'test') + workflowExecutionService.ackTaskReceived(polledTask.taskId) + def taskResult = new TaskResult(polledTask) + taskResult.setStatus(TaskResult.Status.COMPLETED) + workflowExecutionService.updateTask(taskResult) + + and: "Then run a decide to move the workflow forward" + workflowExecutor.decide(workflowInstanceId) + + and: "Get the updated workflow after the task result has been updated" + def updatedWorkflow = workflowExecutionService.getExecutionStatus(workflowInstanceId, true) + + then: "Check that the workflow is created and is not terminal" + workflowInstanceId + workflow + !workflow.getStatus().isTerminal() + !workflow.getReasonForIncompletion() + + and: "Check if the input of the next task to be polled is as expected for a kafka task" + taskInput + taskInput.containsKey('kafka_request') + taskInput['kafka_request'] instanceof Map + objectMapper.writeValueAsString(taskInput) == expectedTaskInput + + and: "Polled task is not null and the workflowInstanceId of the task is same as the workflow created initially" + polledTask + polledTask.workflowInstanceId == workflowInstanceId + + and: "The updated workflow is complete" + updatedWorkflow + updatedWorkflow.status == Workflow.WorkflowStatus.COMPLETED + + } + + def registerKafkaWorkflow() { + System.setProperty("STACK_KAFKA", "test_kafka_topic") + TaskDef templatedTask = new TaskDef() + templatedTask.name = "templated_kafka_task" + templatedTask.retryCount = 0 + templatedTask.ownerEmail = "test@harness.com" + + def kafkaRequest = new HashMap<>() + kafkaRequest["topic"] = '${STACK_KAFKA}' + kafkaRequest["bootStrapServers"] = "localhost:9092" + + def value = new HashMap<>() + value["inputPaths"] = ['${workflow.input.path1}', '${workflow.input.path2}'] + value["requestDetails"] = '${workflow.input.requestDetails}' + value["outputPath"] = '${workflow.input.outputPath}' + kafkaRequest["value"] = value + + templatedTask.inputTemplate["kafka_request"] = kafkaRequest + metadataService.registerTaskDef([templatedTask]) + + WorkflowDef templateWf = new WorkflowDef() + templateWf.name = "template_kafka_workflow" + WorkflowTask wft = new WorkflowTask() + wft.name = templatedTask.name + wft.workflowTaskType = TaskType.KAFKA_PUBLISH + wft.taskReferenceName = "t0" + templateWf.tasks.add(wft) + templateWf.schemaVersion = 2 + templateWf.ownerEmail = "test@harness.com" + metadataService.registerWorkflowDef(templateWf) + } +} diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java new file mode 100644 index 000000000..91fef3029 --- /dev/null +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.tasks.kafka; + +import java.time.Duration; +import java.util.Properties; + +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.LongSerializer; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class KafkaProducerManagerTest { + + @Test + public void testRequestTimeoutSetFromDefault() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(100), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + Properties props = manager.getProducerProperties(input); + assertEquals(props.getProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG), "100"); + } + + @Test + public void testRequestTimeoutSetFromInput() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(100), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + input.setRequestTimeoutMs(200); + Properties props = manager.getProducerProperties(input); + assertEquals(props.getProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG), "200"); + } + + @Test + public void testRequestTimeoutSetFromConfig() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(150), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + Properties props = manager.getProducerProperties(input); + assertEquals(props.getProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG), "150"); + } + + @SuppressWarnings("rawtypes") + @Test(expected = RuntimeException.class) + public void testExecutionException() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(150), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + Producer producer = manager.getProducer(input); + assertNotNull(producer); + } + + @SuppressWarnings("rawtypes") + @Test + public void testCacheInvalidation() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(150), Duration.ofMillis(500), 0, Duration.ofMillis(0)); + KafkaPublishTask.Input input = getInput(); + input.setBootStrapServers(""); + Properties props = manager.getProducerProperties(input); + Producer producerMock = mock(Producer.class); + Producer producer = manager.getFromCache(props, () -> producerMock); + assertNotNull(producer); + verify(producerMock, times(1)).close(); + } + + @Test + public void testMaxBlockMsFromConfig() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(150), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + Properties props = manager.getProducerProperties(input); + assertEquals(props.getProperty(ProducerConfig.MAX_BLOCK_MS_CONFIG), "500"); + } + + @Test + public void testMaxBlockMsFromInput() { + KafkaProducerManager manager = + new KafkaProducerManager( + Duration.ofMillis(150), + Duration.ofMillis(500), + 10, + Duration.ofMillis(120000)); + KafkaPublishTask.Input input = getInput(); + input.setMaxBlockMs(600); + Properties props = manager.getProducerProperties(input); + assertEquals(props.getProperty(ProducerConfig.MAX_BLOCK_MS_CONFIG), "600"); + } + + private KafkaPublishTask.Input getInput() { + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setTopic("testTopic"); + input.setValue("TestMessage"); + input.setKeySerializer(LongSerializer.class.getCanonicalName()); + input.setBootStrapServers("servers"); + return input; + } +} diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java new file mode 100644 index 000000000..08828c444 --- /dev/null +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.tasks.kafka; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.common.serialization.IntegerSerializer; +import org.apache.kafka.common.serialization.LongSerializer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.core.execution.WorkflowExecutor; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings({"unchecked", "rawtypes"}) +@ContextConfiguration(classes = {TestObjectMapperConfiguration.class}) +@RunWith(SpringRunner.class) +public class KafkaPublishTaskTest { + + @Autowired private ObjectMapper objectMapper; + + @Test + public void missingRequest_Fail() { + KafkaPublishTask kafkaPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + TaskModel task = new TaskModel(); + kafkaPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); + } + + @Test + public void missingValue_Fail() { + + TaskModel task = new TaskModel(); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setBootStrapServers("localhost:9092"); + input.setTopic("testTopic"); + + task.getInputData().put(KafkaPublishTask.REQUEST_PARAMETER_NAME, input); + + KafkaPublishTask kPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + kPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); + } + + @Test + public void missingBootStrapServers_Fail() { + + TaskModel task = new TaskModel(); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + + Map value = new HashMap<>(); + input.setValue(value); + input.setTopic("testTopic"); + + task.getInputData().put(KafkaPublishTask.REQUEST_PARAMETER_NAME, input); + + KafkaPublishTask kPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + kPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); + } + + @Test + public void kafkaPublishExecutionException_Fail() + throws ExecutionException, InterruptedException { + + TaskModel task = getTask(); + + KafkaProducerManager producerManager = mock(KafkaProducerManager.class); + KafkaPublishTask kafkaPublishTask = new KafkaPublishTask(producerManager, objectMapper); + + Producer producer = mock(Producer.class); + + when(producerManager.getProducer(any())).thenReturn(producer); + Future publishingFuture = mock(Future.class); + when(producer.send(any())).thenReturn(publishingFuture); + + ExecutionException executionException = mock(ExecutionException.class); + + when(executionException.getMessage()).thenReturn("Execution exception"); + when(publishingFuture.get()).thenThrow(executionException); + + kafkaPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); + assertEquals( + "Failed to invoke kafka task due to: Execution exception", + task.getReasonForIncompletion()); + } + + @Test + public void kafkaPublishUnknownException_Fail() { + + TaskModel task = getTask(); + + KafkaProducerManager producerManager = mock(KafkaProducerManager.class); + KafkaPublishTask kPublishTask = new KafkaPublishTask(producerManager, objectMapper); + + Producer producer = mock(Producer.class); + + when(producerManager.getProducer(any())).thenReturn(producer); + when(producer.send(any())).thenThrow(new RuntimeException("Unknown exception")); + + kPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.FAILED, task.getStatus()); + assertEquals( + "Failed to invoke kafka task due to: Unknown exception", + task.getReasonForIncompletion()); + } + + @Test + public void kafkaPublishSuccess_Completed() { + + TaskModel task = getTask(); + + KafkaProducerManager producerManager = mock(KafkaProducerManager.class); + KafkaPublishTask kPublishTask = new KafkaPublishTask(producerManager, objectMapper); + + Producer producer = mock(Producer.class); + + when(producerManager.getProducer(any())).thenReturn(producer); + when(producer.send(any())).thenReturn(mock(Future.class)); + + kPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.COMPLETED, task.getStatus()); + } + + @Test + public void kafkaPublishSuccess_AsyncComplete() { + + TaskModel task = getTask(); + task.getInputData().put("asyncComplete", true); + + KafkaProducerManager producerManager = mock(KafkaProducerManager.class); + KafkaPublishTask kPublishTask = new KafkaPublishTask(producerManager, objectMapper); + + Producer producer = mock(Producer.class); + + when(producerManager.getProducer(any())).thenReturn(producer); + when(producer.send(any())).thenReturn(mock(Future.class)); + + kPublishTask.start(mock(WorkflowModel.class), task, mock(WorkflowExecutor.class)); + assertEquals(TaskModel.Status.IN_PROGRESS, task.getStatus()); + } + + private TaskModel getTask() { + TaskModel task = new TaskModel(); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setBootStrapServers("localhost:9092"); + + Map value = new HashMap<>(); + + value.put("input_key1", "value1"); + value.put("input_key2", 45.3d); + + input.setValue(value); + input.setTopic("testTopic"); + task.getInputData().put(KafkaPublishTask.REQUEST_PARAMETER_NAME, input); + return task; + } + + @Test + public void integerSerializer_integerObject() { + KafkaPublishTask kPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setKeySerializer(IntegerSerializer.class.getCanonicalName()); + input.setKey(String.valueOf(Integer.MAX_VALUE)); + assertEquals(kPublishTask.getKey(input), Integer.MAX_VALUE); + } + + @Test + public void longSerializer_longObject() { + KafkaPublishTask kPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setKeySerializer(LongSerializer.class.getCanonicalName()); + input.setKey(String.valueOf(Long.MAX_VALUE)); + assertEquals(kPublishTask.getKey(input), Long.MAX_VALUE); + } + + @Test + public void noSerializer_StringObject() { + KafkaPublishTask kPublishTask = + new KafkaPublishTask(getKafkaProducerManager(), objectMapper); + KafkaPublishTask.Input input = new KafkaPublishTask.Input(); + input.setKey("testStringKey"); + assertEquals(kPublishTask.getKey(input), "testStringKey"); + } + + private KafkaProducerManager getKafkaProducerManager() { + return new KafkaProducerManager( + Duration.ofMillis(100), Duration.ofMillis(500), 120000, Duration.ofMillis(10)); + } +} diff --git a/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java new file mode 100644 index 000000000..471bde2ff --- /dev/null +++ b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.core.execution.mapper; + +import java.util.HashMap; +import java.util.List; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.core.utils.IDGenerator; +import com.netflix.conductor.core.utils.ParametersUtils; +import com.netflix.conductor.dao.MetadataDAO; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +public class KafkaPublishTaskMapperTest { + + private IDGenerator idGenerator; + private KafkaPublishTaskMapper kafkaTaskMapper; + + @Rule public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + ParametersUtils parametersUtils = mock(ParametersUtils.class); + MetadataDAO metadataDAO = mock(MetadataDAO.class); + kafkaTaskMapper = new KafkaPublishTaskMapper(parametersUtils, metadataDAO); + idGenerator = new IDGenerator(); + } + + @Test + public void getMappedTasks() { + // Given + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName("kafka_task"); + workflowTask.setType(TaskType.KAFKA_PUBLISH.name()); + workflowTask.setTaskDefinition(new TaskDef("kafka_task")); + String taskId = idGenerator.generate(); + String retriedTaskId = idGenerator.generate(); + + WorkflowModel workflow = new WorkflowModel(); + WorkflowDef workflowDef = new WorkflowDef(); + workflow.setWorkflowDefinition(workflowDef); + + TaskMapperContext taskMapperContext = + TaskMapperContext.newBuilder() + .withWorkflowModel(workflow) + .withTaskDefinition(new TaskDef()) + .withWorkflowTask(workflowTask) + .withTaskInput(new HashMap<>()) + .withRetryCount(0) + .withRetryTaskId(retriedTaskId) + .withTaskId(taskId) + .build(); + + // when + List mappedTasks = kafkaTaskMapper.getMappedTasks(taskMapperContext); + + // Then + assertEquals(1, mappedTasks.size()); + assertEquals(TaskType.KAFKA_PUBLISH.name(), mappedTasks.get(0).getTaskType()); + } + + @Test + public void getMappedTasks_WithoutTaskDef() { + // Given + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName("kafka_task"); + workflowTask.setType(TaskType.KAFKA_PUBLISH.name()); + String taskId = idGenerator.generate(); + String retriedTaskId = idGenerator.generate(); + + WorkflowModel workflow = new WorkflowModel(); + WorkflowDef workflowDef = new WorkflowDef(); + workflow.setWorkflowDefinition(workflowDef); + + TaskDef taskdefinition = new TaskDef(); + String testExecutionNameSpace = "testExecutionNameSpace"; + taskdefinition.setExecutionNameSpace(testExecutionNameSpace); + String testIsolationGroupId = "testIsolationGroupId"; + taskdefinition.setIsolationGroupId(testIsolationGroupId); + TaskMapperContext taskMapperContext = + TaskMapperContext.newBuilder() + .withWorkflowModel(workflow) + .withTaskDefinition(taskdefinition) + .withWorkflowTask(workflowTask) + .withTaskInput(new HashMap<>()) + .withRetryCount(0) + .withRetryTaskId(retriedTaskId) + .withTaskId(taskId) + .build(); + + // when + List mappedTasks = kafkaTaskMapper.getMappedTasks(taskMapperContext); + + // Then + assertEquals(1, mappedTasks.size()); + assertEquals(TaskType.KAFKA_PUBLISH.name(), mappedTasks.get(0).getTaskType()); + assertEquals(testExecutionNameSpace, mappedTasks.get(0).getExecutionNameSpace()); + assertEquals(testIsolationGroupId, mappedTasks.get(0).getIsolationGroupId()); + } +} diff --git a/kafka/src/test/resources/application-integrationtest.properties b/kafka/src/test/resources/application-integrationtest.properties new file mode 100644 index 000000000..7e307590f --- /dev/null +++ b/kafka/src/test/resources/application-integrationtest.properties @@ -0,0 +1,55 @@ +# +# /* +# * Copyright 2021 Netflix, Inc. +# *

+# * 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. +# */ +# + +conductor.db.type=memory +conductor.workflow-execution-lock.type=local_only +conductor.external-payload-storage.type=dummy +conductor.indexing.enabled=false + +conductor.app.stack=test +conductor.app.appId=conductor + +conductor.app.workflow-offset-timeout=30s + +conductor.system-task-workers.enabled=false +conductor.app.system-task-worker-callback-duration=0 + +conductor.app.event-message-indexing-enabled=true +conductor.app.event-execution-indexing-enabled=true + +conductor.workflow-reconciler.enabled=true +conductor.workflow-repair-service.enabled=false + +conductor.app.workflow-execution-lock-enabled=false + +conductor.app.workflow-input-payload-size-threshold=10KB +conductor.app.max-workflow-input-payload-size-threshold=10240KB +conductor.app.workflow-output-payload-size-threshold=10KB +conductor.app.max-workflow-output-payload-size-threshold=10240KB +conductor.app.task-input-payload-size-threshold=10KB +conductor.app.max-task-input-payload-size-threshold=10240KB +conductor.app.task-output-payload-size-threshold=10KB +conductor.app.max-task-output-payload-size-threshold=10240KB +conductor.app.max-workflow-variables-payload-size-threshold=2KB + +conductor.redis.availability-zone=us-east-1c +conductor.redis.data-center-region=us-east-1 +conductor.redis.workflow-namespace-prefix=integration-test +conductor.redis.queue-namespace-prefix=integtest + +conductor.elasticsearch.index-prefix=conductor +conductor.elasticsearch.cluster-health-color=yellow + +management.metrics.export.datadog.enabled=false diff --git a/kafka/src/test/resources/input.json b/kafka/src/test/resources/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/kafka/src/test/resources/output.json b/kafka/src/test/resources/output.json new file mode 100644 index 000000000..c0921ccaa --- /dev/null +++ b/kafka/src/test/resources/output.json @@ -0,0 +1,424 @@ +{ + "imageType": "TEST_SAMPLE", + "case": "two", + "op": { + "TEST_SAMPLE": [ + { + "sourceId": "1413900_10830", + "url": "file/location/a0bdc4d0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_50241", + "url": "file/location/cd4e00a0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "generated-55ee8663-85c2-42d3-aca2-4076707e6d4e", + "url": "file/sample/location/e008d018-63d7-44b2-b07e-c7435430ac71" + }, + { + "sourceId": "generated-14056154-1544-4350-81db-b3751fe44777", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-0b0ae5ea-d5c5-410c-adc9-bf16d2909c2e", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-08869779-614d-417c-bfea-36a3f8f199da", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "generated-e117db45-1c48-45d0-b751-89386eb2d81d", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "f0221421-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/4a009209-002f-4b58-8b96-cb2198f8ba3c" + }, + { + "sourceId": "f0252161-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/55b56298-5e7a-4949-b919-88c5c9557e8e" + }, + { + "sourceId": "f038d070-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/3c4804f4-e826-436f-90c9-52b8d9266d52" + }, + { + "sourceId": "f04e0621-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/689283a1-1816-48ef-83da-7f9ac874bf45" + }, + { + "sourceId": "f04ddf10-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/586666ae-7321-445a-80b6-323c8c241ecd" + }, + { + "sourceId": "f05950c0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/31795cc4-2590-4b20-a617-deaa18301f99" + }, + { + "sourceId": "1413900_46819", + "url": "file/location/c74497a0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_11177", + "url": "file/location/a231c730-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_48713", + "url": "file/location/ca638ae0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_48525", + "url": "file/location/ca0c9140-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_73303", + "url": "file/location/d5943a40-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "1413900_55202", + "url": "file/location/d1a4d7a0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "generated-61413adf-3c10-4484-b25d-e238df898f45", + "url": "file/sample/location/e008d018-63d7-44b2-b07e-c7435430ac71" + }, + { + "sourceId": "generated-addca397-f050-4339-ae86-9ba8c4e1b0d5", + "url": "file/sample/location/838a0ddb-a315-453a-8b8a-fa795f9d7691" + }, + { + "sourceId": "generated-e4de9810-0f69-4593-8926-01ed82cbebcb", + "url": "file/sample/location/838a0ddb-a315-453a-8b8a-fa795f9d7691" + }, + { + "sourceId": "generated-e16e2074-7af6-4700-ab05-ca41ba9c9ab4", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-341c86f8-57a5-40e1-8842-3eb41dd9f528", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-88c2ea9b-cef7-4120-8043-b92713d8fade", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-3f6a731f-3c92-4677-9923-f80b8a6be632", + "url": "file/sample/location/3881aea9-a731-4e22-9ead-2d6eccc51140" + }, + { + "sourceId": "generated-1508b871-64de-47ce-8b07-76c5cb3f3e1e", + "url": "file/sample/location/a2e4195f-3900-45b4-9335-45f85fca6467" + }, + { + "sourceId": "generated-1406dce8-7b9c-4956-a7e8-78721c476ce9", + "url": "file/sample/location/a2e4195f-3900-45b4-9335-45f85fca6467" + }, + { + "sourceId": "f0206671-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/35ebee36-3072-44c5-abb5-702a5a3b1a91" + }, + { + "sourceId": "f01f5501-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/d3a9133d-c681-4910-a769-8195526ae634" + }, + { + "sourceId": "f022b060-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/8fc1413d-170e-4644-a554-5e0c596b225c" + }, + { + "sourceId": "f02fa8b1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/35bed0a2-7def-457b-bded-4f4d7d94f76e" + }, + { + "sourceId": "f031f2a0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/a5a2ea1f-8d13-429c-a44d-3057d21f608a" + }, + { + "sourceId": "f0424650-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/1c599ffc-4f10-4c0b-8d9a-ae41c7256113" + }, + { + "sourceId": "f04ec970-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/8404a421-e1a6-41cf-af63-a35ccb474457" + }, + { + "sourceId": "1413900_47197", + "url": "file/location/c81b6fa0-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "generated-2a63c0c8-62ea-44a4-a33b-f0b3047e8b00", + "url": "file/sample/location/e008d018-63d7-44b2-b07e-c7435430ac71" + }, + { + "sourceId": "generated-b27face7-3589-4209-944a-5153b20c5996", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-144675b3-9321-48d2-8b5b-e19a40d30ef2", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-8cbe821e-b1fb-48ce-beb5-735319af4db6", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-ecc4ea47-9bad-4b91-97c7-35f4ea6fb479", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-c1eb9ed0-8560-4e09-a748-f926edb7cdc2", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "generated-6bed81fd-c777-4c61-8da1-0bb7f7cf0082", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "generated-852e5510-dd5d-4900-a614-854148fcc716", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "generated-f4dedcb7-37c9-4ba9-ab37-64ec9be7c882", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "f0259691-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/721bc0de-e75f-4386-8b2e-ca84eb653596" + }, + { + "sourceId": "f02b3be1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/d2043b17-8ce5-42ee-a5e4-81c68f0c4838" + }, + { + "sourceId": "f02b62f0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/63931561-3b5b-4ffe-af47-da2c9de94684" + }, + { + "sourceId": "f0315660-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/d99ed629-2885-4e4a-8a1b-22e487b875fa" + }, + { + "sourceId": "f0306c00-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/6f8e673a-7003-44aa-96b9-e2ed8a4654ff" + }, + { + "sourceId": "f033c760-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/627c00f9-14b3-4057-b6e2-0f962ad0308e" + }, + { + "sourceId": "f03526f1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/fafabaf9-fe58-4a9a-b555-026521aeb2fe" + }, + { + "sourceId": "f03acc41-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/6c9fed2c-558a-4db3-8360-659b5e8c46e4" + }, + { + "sourceId": "f0463df1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/e9fb83d2-5f14-4442-92b5-67e613f2e35f" + }, + { + "sourceId": "f04fb3d0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/e7a0f82f-be8d-4ada-a4b1-13e8165e08be" + }, + { + "sourceId": "f05272f0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/9aba488a-22b3-4932-85a7-52c461203541" + }, + { + "sourceId": "f0581841-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/457415f6-6d0c-4304-8533-0d5b43fac564" + }, + { + "sourceId": "generated-8fefb48c-6fde-4fd6-8f33-a1f3f3b62105", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-30c61aa5-f5bd-4077-8c32-336b87acbe96", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-d5da37db-d486-46d4-8f7d-1e0710a77eb5", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-77af26fe-9e22-48af-99e3-f63f10fbe6de", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-2e807016-3d11-4b60-bec7-c380a608b67d", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-615d02e9-62c2-43ab-9df7-753b6b8e2c22", + "url": "file/sample/location/519f6c80-96ef-440f-9d37-ccf36c7d1e5d" + }, + { + "sourceId": "generated-3e1600fd-a626-4ee6-972b-5f0187e96c38", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "generated-1dcb208c-6a58-4334-a60c-6fb54c8a2af5", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "f024ac30-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/0af2107b-4231-4d23-bef3-4e417ac6c5d3" + }, + { + "sourceId": "f0282ea1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/0f592681-fd23-4194-ae43-42f61c664485" + }, + { + "sourceId": "f02c4d50-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/ec46b9a3-99af-410a-af7d-726f8854909f" + }, + { + "sourceId": "f02b8a00-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/aed7e5da-b524-4d41-b264-28ce615ec826" + }, + { + "sourceId": "f02b14d1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/b88c9055-ab0d-4d27-a405-265ba2a15f0c" + }, + { + "sourceId": "f03044f1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/fb8c4df9-d59e-4ac3-880e-4ea94cd880a4" + }, + { + "sourceId": "f034ffe1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/59f3fbe8-b300-4861-9b2f-dac7b15aea7d" + }, + { + "sourceId": "f03c2bd0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/19a06d54-41ed-419d-9947-f10cd5f0d85c" + }, + { + "sourceId": "f03fae41-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/a9a48a62-7d62-4f67-b281-cc6fdc1e722c" + }, + { + "sourceId": "f0455390-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/0aeffc0a-a5ad-46ff-abab-1b3bc6a5840a" + }, + { + "sourceId": "f04b1ff1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/9a08aaed-c125-48f7-9d1d-fd11266c2b12" + }, + { + "sourceId": "f04cf4b1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/17a6e0f9-aa64-411f-9af7-837c84f7443f" + }, + { + "sourceId": "f0511360-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/fb633c73-cb33-4806-bc08-049024644856" + }, + { + "sourceId": "f0538460-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/a7012248-6769-42da-a6c8-d4b831f6efce" + }, + { + "sourceId": "f058db91-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/bcf71522-6168-48c4-86c9-995bca60ae51" + }, + { + "sourceId": "generated-adf005c4-95c1-4904-9968-09cc19a26bfe", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-c4d367a4-4cdc-412e-af79-09b227f2e3ba", + "url": "file/sample/location/3d927190-1c4d-4af2-91cf-2968d3ccfe70" + }, + { + "sourceId": "generated-48dba018-f884-49db-b87e-67274e244c8f", + "url": "file/sample/location/4bce4154-fb4b-4f0a-887d-a0cd12d4d214" + }, + { + "sourceId": "generated-26700b83-4892-420e-8b46-1ee21eba75fb", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "generated-632f3198-c0dc-4348-974f-51684d4e443e", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "generated-86e2dd1d-1aa4-4dbe-b37b-b488f5dd1c70", + "url": "file/sample/location/e87da4d1-72da-47a3-801d-43e01c050c89" + }, + { + "sourceId": "f04134e0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/ff8f59bf-7757-4d51-a7e4-619f3e8ffaf2" + }, + { + "sourceId": "f04f65b0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/d66467d1-3ac6-4041-8d15-e722ee07231f" + }, + { + "sourceId": "1413900_15255", + "url": "file/location/a9e20260-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "generated-e953493b-cbe3-4319-885e-00c82089c76c", + "url": "file/sample/location/ec16facd-86e3-4c3f-8dfb-7a2ad3a4e18c" + }, + { + "sourceId": "generated-65c54676-3adb-4ef0-b65e-8e2a49533cbf", + "url": "file/sample/location/07ec28a1-189e-4f2a-9dd5-f3ca68ce977d" + }, + { + "sourceId": "f02ac6b0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/21568877-07a5-411f-9715-5e92806c4448" + }, + { + "sourceId": "f02fcfc1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/f3b1f1a2-48d3-475d-a607-2e5a1fe532e7" + }, + { + "sourceId": "f03526f0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/84a40c66-d925-4a4a-ba62-8491d26e29e9" + }, + { + "sourceId": "f03e75c1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/e84c00e8-a148-46cf-9a0b-431c4c2aeb08" + }, + { + "sourceId": "f0429471-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/178de9fa-7cc8-457a-8fb6-5c080e6163ea" + }, + { + "sourceId": "f047eba0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/18d153aa-e13b-4264-ae03-f3da75eb425b" + }, + { + "sourceId": "f04fdae0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/7c843e53-8d87-47cf-bca5-1a02e7f5e33f" + }, + { + "sourceId": "f0553210-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/26bacd65-9082-4d83-9506-90e5f1ccd16a" + }, + { + "sourceId": "1413900_84904", + "url": "file/location/d8f7b090-5315-11e8-bf88-0efd527701fc" + }, + { + "sourceId": "generated-84adc784-8d7d-4088-ba51-16fde57fbc21", + "url": "file/sample/location/3881aea9-a731-4e22-9ead-2d6eccc51140" + }, + { + "sourceId": "generated-9e49c58b-0b33-4daf-a39a-8fc91e302328", + "url": "file/sample/location/4bce4154-fb4b-4f0a-887d-a0cd12d4d214" + }, + { + "sourceId": "f02dd3f1-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/8937b328-8f0d-4762-8d1f-7d7bc80c3d2e" + }, + { + "sourceId": "f03240c0-86e8-11e8-af77-0a2ba4eae3ec", + "url": "file/test/location/aab6e386-4d59-4b40-b257-9aed12a45446" + } + ] + } +} \ No newline at end of file diff --git a/kafka/src/test/resources/simple_json_jq_transform_integration_test.json b/kafka/src/test/resources/simple_json_jq_transform_integration_test.json new file mode 100644 index 000000000..dc394775d --- /dev/null +++ b/kafka/src/test/resources/simple_json_jq_transform_integration_test.json @@ -0,0 +1,32 @@ +{ + "name": "test_json_jq_transform_wf", + "version": 1, + "tasks": [ + { + "name": "jq", + "taskReferenceName": "jq_1", + "inputParameters": { + "input": "${workflow.input}", + "queryExpression": ".input as $_ | { out: ($_.in1.array + $_.in2.array) }" + }, + "type": "JSON_JQ_TRANSFORM", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a623e8486..40801041b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -63,6 +63,7 @@ include 'java-sdk' // community modules include 'workflow-event-listener' include 'test-util' +include 'kafka' include 'test-harness' From 42d7a78fc665ed5d1a4014e1d42555bea8b3a4b1 Mon Sep 17 00:00:00 2001 From: Luis Lainez Date: Mon, 18 Dec 2023 08:00:22 +1100 Subject: [PATCH 034/202] Cleanup previous changes --- .../conductor/redis/dynoqueue/ConfigurationHostSupplier.java | 3 --- server/src/main/resources/application.properties | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java index 2ae22e7d0..9e32a1d5f 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java @@ -40,9 +40,6 @@ public List getHosts() { } private List parseHostsFromConfig() { - System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - System.out.println("Properties in ConfigurationHostSupplier: "); - System.out.println(properties); String hosts = properties.getHosts(); if (hosts == null) { String message = diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 9889a2022..09b8f22b4 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -15,7 +15,7 @@ spring.application.name=conductor springdoc.api-docs.path=/api-docs loadSample=true -conductor.db.type=redis_standalone +conductor.db.type=memory conductor.queue.type=redis_standalone conductor.indexing.enabled=false From a138919249ab5334c831c25db142e003f0ef762a Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 18:40:41 -0800 Subject: [PATCH 035/202] postgres and mysql persistence --- common-persistence/build.gradle | 10 + common-persistence/dependencies.lock | 159 +++ .../conductor/dao/ExecutionDAOTest.java | 439 +++++++ .../com/netflix/conductor/dao/TestBase.java | 14 + gradle.properties | 2 + mysql-persistence/build.gradle | 45 + mysql-persistence/dependencies.lock | 991 +++++++++++++++ .../mysql/config/MySQLConfiguration.java | 114 ++ .../mysql/config/MySQLProperties.java | 41 + .../conductor/mysql/dao/MySQLBaseDAO.java | 255 ++++ .../mysql/dao/MySQLExecutionDAO.java | 1062 ++++++++++++++++ .../conductor/mysql/dao/MySQLMetadataDAO.java | 560 +++++++++ .../conductor/mysql/dao/MySQLQueueDAO.java | 394 ++++++ .../conductor/mysql/util/ExecuteFunction.java | 25 + .../conductor/mysql/util/LazyToString.java | 32 + .../netflix/conductor/mysql/util/Query.java | 619 ++++++++++ .../conductor/mysql/util/QueryFunction.java | 25 + .../mysql/util/ResultSetHandler.java | 26 + .../mysql/util/TransactionalFunction.java | 26 + .../db/migration/V1__initial_schema.sql | 172 +++ .../V2__queue_message_timestamps.sql | 2 + .../db/migration/V3__queue_add_priority.sql | 17 + .../V4__1009_Fix_MySQLExecutionDAO_Index.sql | 14 + .../db/migration/V5__correlation_id_index.sql | 13 + .../V6__new_qm_index_with_priority.sql | 13 + .../db/migration/V7__new_queue_message_pk.sql | 24 + .../resources/db/migration/V8__update_pk.sql | 103 ++ .../mysql/dao/MySQLExecutionDAOTest.java | 80 ++ .../mysql/dao/MySQLMetadataDAOTest.java | 321 +++++ .../mysql/dao/MySQLQueueDAOTest.java | 384 ++++++ .../grpc/mysql/MySQLGrpcEndToEndTest.java | 47 + .../src/test/resources/application.properties | 6 + postgres-persistence/build.gradle | 43 + postgres-persistence/dependencies.lock | 991 +++++++++++++++ .../config/PostgresConfiguration.java | 144 +++ .../postgres/config/PostgresProperties.java | 54 + .../postgres/dao/PostgresBaseDAO.java | 255 ++++ .../postgres/dao/PostgresExecutionDAO.java | 1099 +++++++++++++++++ .../postgres/dao/PostgresIndexDAO.java | 302 +++++ .../postgres/dao/PostgresMetadataDAO.java | 579 +++++++++ .../postgres/dao/PostgresQueueDAO.java | 501 ++++++++ .../postgres/util/ExecuteFunction.java | 25 + .../postgres/util/ExecutorsUtil.java | 36 + .../conductor/postgres/util/LazyToString.java | 32 + .../util/PostgresIndexQueryBuilder.java | 220 ++++ .../conductor/postgres/util/Query.java | 647 ++++++++++ .../postgres/util/QueryFunction.java | 25 + .../postgres/util/ResultSetHandler.java | 26 + .../postgres/util/TransactionalFunction.java | 26 + .../migration_postgres/V1__initial_schema.sql | 173 +++ ...2__1009_Fix_PostgresExecutionDAO_Index.sql | 3 + .../V3__correlation_id_index.sql | 3 + .../V4__new_qm_index_with_priority.sql | 3 + .../V5__new_queue_message_pk.sql | 11 + .../db/migration_postgres/V6__update_pk.sql | 77 ++ .../V7__new_qm_index_desc_priority.sql | 3 + .../db/migration_postgres/V8__indexing.sql | 47 + .../dao/PostgresExecutionDAOTest.java | 113 ++ .../postgres/dao/PostgresIndexDAOTest.java | 405 ++++++ .../postgres/dao/PostgresMetadataDAOTest.java | 323 +++++ .../postgres/dao/PostgresQueueDAOTest.java | 417 +++++++ .../postgres/performance/PerformanceTest.java | 453 +++++++ .../util/PostgresIndexQueryBuilderTest.java | 285 +++++ .../postgres/PostgresGrpcEndToEndTest.java | 50 + .../src/test/resources/application.properties | 8 + settings.gradle | 3 + 66 files changed, 13417 insertions(+) create mode 100644 common-persistence/build.gradle create mode 100644 common-persistence/dependencies.lock create mode 100644 common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java create mode 100644 common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java create mode 100644 gradle.properties create mode 100644 mysql-persistence/build.gradle create mode 100644 mysql-persistence/dependencies.lock create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java create mode 100644 mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java create mode 100644 mysql-persistence/src/main/resources/db/migration/V1__initial_schema.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V2__queue_message_timestamps.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V3__queue_add_priority.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V4__1009_Fix_MySQLExecutionDAO_Index.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V5__correlation_id_index.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V6__new_qm_index_with_priority.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V7__new_queue_message_pk.sql create mode 100644 mysql-persistence/src/main/resources/db/migration/V8__update_pk.sql create mode 100644 mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java create mode 100644 mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java create mode 100644 mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java create mode 100644 mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java create mode 100644 mysql-persistence/src/test/resources/application.properties create mode 100644 postgres-persistence/build.gradle create mode 100644 postgres-persistence/dependencies.lock create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java create mode 100644 postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V1__initial_schema.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V2__1009_Fix_PostgresExecutionDAO_Index.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V3__correlation_id_index.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V4__new_qm_index_with_priority.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V5__new_queue_message_pk.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V6__update_pk.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V7__new_qm_index_desc_priority.sql create mode 100644 postgres-persistence/src/main/resources/db/migration_postgres/V8__indexing.sql create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java create mode 100644 postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java create mode 100644 postgres-persistence/src/test/resources/application.properties diff --git a/common-persistence/build.gradle b/common-persistence/build.gradle new file mode 100644 index 000000000..9d09a58ac --- /dev/null +++ b/common-persistence/build.gradle @@ -0,0 +1,10 @@ +dependencies { + + implementation project(':conductor-common') + implementation project(':conductor-core') + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + implementation "org.apache.commons:commons-lang3" + +} \ No newline at end of file diff --git a/common-persistence/dependencies.lock b/common-persistence/dependencies.lock new file mode 100644 index 000000000..0a6426ed9 --- /dev/null +++ b/common-persistence/dependencies.lock @@ -0,0 +1,159 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + }, + "testRuntimeClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + } +} \ No newline at end of file diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java new file mode 100644 index 000000000..32415e35c --- /dev/null +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -0,0 +1,439 @@ +/* + *

+ * 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 com.netflix.conductor.dao; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; + +import static org.junit.Assert.*; + +public abstract class ExecutionDAOTest { + + protected abstract ExecutionDAO getExecutionDAO(); + + protected ConcurrentExecutionLimitDAO getConcurrentExecutionLimitDAO() { + return (ConcurrentExecutionLimitDAO) getExecutionDAO(); + } + + @Rule public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void testTaskExceedsLimit() { + TaskDef taskDefinition = new TaskDef(); + taskDefinition.setName("task100"); + taskDefinition.setConcurrentExecLimit(1); + + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName("task1"); + workflowTask.setTaskDefinition(taskDefinition); + workflowTask.setTaskDefinition(taskDefinition); + + List tasks = new LinkedList<>(); + for (int i = 0; i < 15; i++) { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(i + 1); + task.setTaskId("t_" + i); + task.setWorkflowInstanceId("workflow_" + i); + task.setReferenceTaskName("task1"); + task.setTaskDefName("task100"); + tasks.add(task); + task.setStatus(TaskModel.Status.SCHEDULED); + task.setWorkflowTask(workflowTask); + } + + getExecutionDAO().createTasks(tasks); + assertFalse(getConcurrentExecutionLimitDAO().exceedsLimit(tasks.get(0))); + tasks.get(0).setStatus(TaskModel.Status.IN_PROGRESS); + getExecutionDAO().updateTask(tasks.get(0)); + + for (TaskModel task : tasks) { + assertTrue(getConcurrentExecutionLimitDAO().exceedsLimit(task)); + } + } + + @Test + public void testCreateTaskException() { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(UUID.randomUUID().toString()); + task.setTaskDefName("task1"); + + expectedException.expect(NonTransientException.class); + expectedException.expectMessage("Workflow instance id cannot be null"); + getExecutionDAO().createTasks(Collections.singletonList(task)); + + task.setWorkflowInstanceId(UUID.randomUUID().toString()); + expectedException.expect(NonTransientException.class); + expectedException.expectMessage("Task reference name cannot be null"); + getExecutionDAO().createTasks(Collections.singletonList(task)); + } + + @Test + public void testCreateTaskException2() { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(UUID.randomUUID().toString()); + task.setTaskDefName("task1"); + task.setWorkflowInstanceId(UUID.randomUUID().toString()); + + expectedException.expect(NonTransientException.class); + expectedException.expectMessage("Task reference name cannot be null"); + getExecutionDAO().createTasks(Collections.singletonList(task)); + } + + @Test + public void testTaskCreateDups() { + List tasks = new LinkedList<>(); + String workflowId = UUID.randomUUID().toString(); + + for (int i = 0; i < 3; i++) { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(i + 1); + task.setTaskId(workflowId + "_t" + i); + task.setReferenceTaskName("t" + i); + task.setRetryCount(0); + task.setWorkflowInstanceId(workflowId); + task.setTaskDefName("task" + i); + task.setStatus(TaskModel.Status.IN_PROGRESS); + tasks.add(task); + } + + // Let's insert a retried task + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(workflowId + "_t" + 2); + task.setReferenceTaskName("t" + 2); + task.setRetryCount(1); + task.setWorkflowInstanceId(workflowId); + task.setTaskDefName("task" + 2); + task.setStatus(TaskModel.Status.IN_PROGRESS); + tasks.add(task); + + // Duplicate task! + task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(workflowId + "_t" + 1); + task.setReferenceTaskName("t" + 1); + task.setRetryCount(0); + task.setWorkflowInstanceId(workflowId); + task.setTaskDefName("task" + 1); + task.setStatus(TaskModel.Status.IN_PROGRESS); + tasks.add(task); + + List created = getExecutionDAO().createTasks(tasks); + assertEquals(tasks.size() - 1, created.size()); // 1 less + + Set srcIds = + tasks.stream() + .map(t -> t.getReferenceTaskName() + "." + t.getRetryCount()) + .collect(Collectors.toSet()); + Set createdIds = + created.stream() + .map(t -> t.getReferenceTaskName() + "." + t.getRetryCount()) + .collect(Collectors.toSet()); + + assertEquals(srcIds, createdIds); + + List pending = getExecutionDAO().getPendingTasksByWorkflow("task0", workflowId); + assertNotNull(pending); + assertEquals(1, pending.size()); + assertTrue(EqualsBuilder.reflectionEquals(tasks.get(0), pending.get(0))); + + List found = getExecutionDAO().getTasks(tasks.get(0).getTaskDefName(), null, 1); + assertNotNull(found); + assertEquals(1, found.size()); + assertTrue(EqualsBuilder.reflectionEquals(tasks.get(0), found.get(0))); + } + + @Test + public void testTaskOps() { + List tasks = new LinkedList<>(); + String workflowId = UUID.randomUUID().toString(); + + for (int i = 0; i < 3; i++) { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(workflowId + "_t" + i); + task.setReferenceTaskName("testTaskOps" + i); + task.setRetryCount(0); + task.setWorkflowInstanceId(workflowId); + task.setTaskDefName("testTaskOps" + i); + task.setStatus(TaskModel.Status.IN_PROGRESS); + tasks.add(task); + } + + for (int i = 0; i < 3; i++) { + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId("x" + workflowId + "_t" + i); + task.setReferenceTaskName("testTaskOps" + i); + task.setRetryCount(0); + task.setWorkflowInstanceId("x" + workflowId); + task.setTaskDefName("testTaskOps" + i); + task.setStatus(TaskModel.Status.IN_PROGRESS); + getExecutionDAO().createTasks(Collections.singletonList(task)); + } + + List created = getExecutionDAO().createTasks(tasks); + assertEquals(tasks.size(), created.size()); + + List pending = + getExecutionDAO().getPendingTasksForTaskType(tasks.get(0).getTaskDefName()); + assertNotNull(pending); + assertEquals(2, pending.size()); + // Pending list can come in any order. finding the one we are looking for and then + // comparing + TaskModel matching = + pending.stream() + .filter(task -> task.getTaskId().equals(tasks.get(0).getTaskId())) + .findAny() + .get(); + assertTrue(EqualsBuilder.reflectionEquals(matching, tasks.get(0))); + + for (int i = 0; i < 3; i++) { + TaskModel found = getExecutionDAO().getTask(workflowId + "_t" + i); + assertNotNull(found); + found.getOutputData().put("updated", true); + found.setStatus(TaskModel.Status.COMPLETED); + getExecutionDAO().updateTask(found); + } + + List taskIds = + tasks.stream().map(TaskModel::getTaskId).collect(Collectors.toList()); + List found = getExecutionDAO().getTasks(taskIds); + assertEquals(taskIds.size(), found.size()); + found.forEach( + task -> { + assertTrue(task.getOutputData().containsKey("updated")); + assertEquals(true, task.getOutputData().get("updated")); + boolean removed = getExecutionDAO().removeTask(task.getTaskId()); + assertTrue(removed); + }); + + found = getExecutionDAO().getTasks(taskIds); + assertTrue(found.isEmpty()); + } + + @Test + public void testPending() { + WorkflowDef def = new WorkflowDef(); + def.setName("pending_count_test"); + + WorkflowModel workflow = createTestWorkflow(); + workflow.setWorkflowDefinition(def); + + List workflowIds = generateWorkflows(workflow, 10); + long count = getExecutionDAO().getPendingWorkflowCount(def.getName()); + assertEquals(10, count); + + for (int i = 0; i < 10; i++) { + getExecutionDAO().removeFromPendingWorkflow(def.getName(), workflowIds.get(i)); + } + + count = getExecutionDAO().getPendingWorkflowCount(def.getName()); + assertEquals(0, count); + } + + @Test + public void complexExecutionTest() { + WorkflowModel workflow = createTestWorkflow(); + int numTasks = workflow.getTasks().size(); + + String workflowId = getExecutionDAO().createWorkflow(workflow); + assertEquals(workflow.getWorkflowId(), workflowId); + + List created = getExecutionDAO().createTasks(workflow.getTasks()); + assertEquals(workflow.getTasks().size(), created.size()); + + WorkflowModel workflowWithTasks = + getExecutionDAO().getWorkflow(workflow.getWorkflowId(), true); + assertEquals(workflowId, workflowWithTasks.getWorkflowId()); + assertEquals(numTasks, workflowWithTasks.getTasks().size()); + + WorkflowModel found = getExecutionDAO().getWorkflow(workflowId, false); + assertTrue(found.getTasks().isEmpty()); + + workflow.getTasks().clear(); + assertEquals(workflow, found); + + workflow.getInput().put("updated", true); + getExecutionDAO().updateWorkflow(workflow); + found = getExecutionDAO().getWorkflow(workflowId); + assertNotNull(found); + assertTrue(found.getInput().containsKey("updated")); + assertEquals(true, found.getInput().get("updated")); + + List running = + getExecutionDAO() + .getRunningWorkflowIds( + workflow.getWorkflowName(), workflow.getWorkflowVersion()); + assertNotNull(running); + assertTrue(running.isEmpty()); + + workflow.setStatus(WorkflowModel.Status.RUNNING); + getExecutionDAO().updateWorkflow(workflow); + + running = + getExecutionDAO() + .getRunningWorkflowIds( + workflow.getWorkflowName(), workflow.getWorkflowVersion()); + assertNotNull(running); + assertEquals(1, running.size()); + assertEquals(workflow.getWorkflowId(), running.get(0)); + + List pending = + getExecutionDAO() + .getPendingWorkflowsByType( + workflow.getWorkflowName(), workflow.getWorkflowVersion()); + assertNotNull(pending); + assertEquals(1, pending.size()); + assertEquals(3, pending.get(0).getTasks().size()); + pending.get(0).getTasks().clear(); + assertEquals(workflow, pending.get(0)); + + workflow.setStatus(WorkflowModel.Status.COMPLETED); + getExecutionDAO().updateWorkflow(workflow); + running = + getExecutionDAO() + .getRunningWorkflowIds( + workflow.getWorkflowName(), workflow.getWorkflowVersion()); + assertNotNull(running); + assertTrue(running.isEmpty()); + + List bytime = + getExecutionDAO() + .getWorkflowsByType( + workflow.getWorkflowName(), + System.currentTimeMillis(), + System.currentTimeMillis() + 100); + assertNotNull(bytime); + assertTrue(bytime.isEmpty()); + + bytime = + getExecutionDAO() + .getWorkflowsByType( + workflow.getWorkflowName(), + workflow.getCreateTime() - 10, + workflow.getCreateTime() + 10); + assertNotNull(bytime); + assertEquals(1, bytime.size()); + } + + protected WorkflowModel createTestWorkflow() { + WorkflowDef def = new WorkflowDef(); + def.setName("Junit Workflow"); + def.setVersion(3); + def.setSchemaVersion(2); + + WorkflowModel workflow = new WorkflowModel(); + workflow.setWorkflowDefinition(def); + workflow.setCorrelationId("correlationX"); + workflow.setCreatedBy("junit_tester"); + workflow.setEndTime(200L); + + Map input = new HashMap<>(); + input.put("param1", "param1 value"); + input.put("param2", 100); + workflow.setInput(input); + + Map output = new HashMap<>(); + output.put("ouput1", "output 1 value"); + output.put("op2", 300); + workflow.setOutput(output); + + workflow.setOwnerApp("workflow"); + workflow.setParentWorkflowId("parentWorkflowId"); + workflow.setParentWorkflowTaskId("parentWFTaskId"); + workflow.setReasonForIncompletion("missing recipe"); + workflow.setReRunFromWorkflowId("re-run from id1"); + workflow.setCreateTime(90L); + workflow.setStatus(WorkflowModel.Status.FAILED); + workflow.setWorkflowId(UUID.randomUUID().toString()); + + List tasks = new LinkedList<>(); + + TaskModel task = new TaskModel(); + task.setScheduledTime(1L); + task.setSeq(1); + task.setTaskId(UUID.randomUUID().toString()); + task.setReferenceTaskName("t1"); + task.setWorkflowInstanceId(workflow.getWorkflowId()); + task.setTaskDefName("task1"); + + TaskModel task2 = new TaskModel(); + task2.setScheduledTime(2L); + task2.setSeq(2); + task2.setTaskId(UUID.randomUUID().toString()); + task2.setReferenceTaskName("t2"); + task2.setWorkflowInstanceId(workflow.getWorkflowId()); + task2.setTaskDefName("task2"); + + TaskModel task3 = new TaskModel(); + task3.setScheduledTime(2L); + task3.setSeq(3); + task3.setTaskId(UUID.randomUUID().toString()); + task3.setReferenceTaskName("t3"); + task3.setWorkflowInstanceId(workflow.getWorkflowId()); + task3.setTaskDefName("task3"); + + tasks.add(task); + tasks.add(task2); + tasks.add(task3); + + workflow.setTasks(tasks); + + workflow.setUpdatedBy("junit_tester"); + workflow.setUpdatedTime(800L); + + return workflow; + } + + protected List generateWorkflows(WorkflowModel base, int count) { + List workflowIds = new ArrayList<>(); + for (int i = 0; i < count; i++) { + String workflowId = UUID.randomUUID().toString(); + base.setWorkflowId(workflowId); + base.setCorrelationId("corr001"); + base.setStatus(WorkflowModel.Status.RUNNING); + getExecutionDAO().createWorkflow(base); + workflowIds.add(workflowId); + } + return workflowIds; + } +} diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java new file mode 100644 index 000000000..24b3a52de --- /dev/null +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java @@ -0,0 +1,14 @@ +/* + *

+ * 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 com.netflix.conductor.dao; + +public class TestBase {} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..5fbb8ae30 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx1g diff --git a/mysql-persistence/build.gradle b/mysql-persistence/build.gradle new file mode 100644 index 000000000..3b060c6bc --- /dev/null +++ b/mysql-persistence/build.gradle @@ -0,0 +1,45 @@ +dependencies { + + implementation project(':conductor-common-persistence') + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.retry:spring-retry' + + implementation "com.google.guava:guava:${revGuava}" + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + + implementation "org.apache.commons:commons-lang3" + + implementation "mysql:mysql-connector-java:8.0.33" + implementation "org.springframework.boot:spring-boot-starter-jdbc" + implementation "org.flywaydb:flyway-mysql" + + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" + + + testImplementation "org.elasticsearch:elasticsearch:6.8.23" + testImplementation "org.elasticsearch.client:transport:6.8.23" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" + testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" + testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" + + testImplementation "org.testcontainers:mysql:${revTestContainer}" + + testImplementation project(':conductor-server') + testImplementation project(':conductor-client') + testImplementation project(':conductor-grpc-client') + testImplementation project(':conductor-es6-persistence') + + testImplementation project(':conductor-test-util').sourceSets.test.output + testImplementation project(':conductor-common-persistence').sourceSets.test.output + +} + +test { + //the MySQL unit tests must run within the same JVM to share the same embedded DB + maxParallelForks = 1 +} diff --git a/mysql-persistence/dependencies.lock b/mysql-persistence/dependencies.lock new file mode 100644 index 000000000..2e635692f --- /dev/null +++ b/mysql-persistence/dependencies.lock @@ -0,0 +1,991 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "3.1.4" + } + }, + "compileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "mysql:mysql-connector-java": { + "locked": "8.0.33" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.flywaydb:flyway-mysql": { + "locked": "9.16.3" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "locked": "2.0.3" + } + }, + "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "mysql:mysql-connector-java": { + "locked": "8.0.33" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.flywaydb:flyway-mysql": { + "locked": "9.16.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + } + }, + "testCompileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "junit:junit": { + "locked": "4.13.2" + }, + "mysql:mysql-connector-java": { + "locked": "8.0.33" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "locked": "6.8.23" + }, + "org.elasticsearch:elasticsearch": { + "locked": "6.8.23" + }, + "org.flywaydb:flyway-mysql": { + "locked": "9.16.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + } + }, + "testRuntimeClasspath": { + "com.amazonaws:aws-java-sdk-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "3.22.3" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.eureka:eureka-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.10.10" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.sun.jersey:jersey-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.19.4" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:javax.ws.rs-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.1.1" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "mysql:mysql-connector-java": { + "locked": "8.0.33" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch:elasticsearch": { + "locked": "6.8.23" + }, + "org.flywaydb:flyway-mysql": { + "locked": "9.16.3" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.glassfish.jersey.core:jersey-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "3.1.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.slf4j:slf4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.0.9" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:mysql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + } +} \ No newline at end of file diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java new file mode 100644 index 000000000..17dd3cdb5 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java @@ -0,0 +1,114 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.config; + +import java.sql.SQLException; +import java.util.Optional; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Import; +import org.springframework.retry.RetryContext; +import org.springframework.retry.backoff.NoBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.mysql.dao.MySQLExecutionDAO; +import com.netflix.conductor.mysql.dao.MySQLMetadataDAO; +import com.netflix.conductor.mysql.dao.MySQLQueueDAO; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static com.mysql.cj.exceptions.MysqlErrorNumbers.ER_LOCK_DEADLOCK; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(MySQLProperties.class) +@ConditionalOnProperty(name = "conductor.db.type", havingValue = "mysql") +// Import the DataSourceAutoConfiguration when mysql database is selected. +// By default the datasource configuration is excluded in the main module. +@Import(DataSourceAutoConfiguration.class) +public class MySQLConfiguration { + + @Bean + @DependsOn({"flyway", "flywayInitializer"}) + public MySQLMetadataDAO mySqlMetadataDAO( + @Qualifier("mysqlRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper, + DataSource dataSource, + MySQLProperties properties) { + return new MySQLMetadataDAO(retryTemplate, objectMapper, dataSource, properties); + } + + @Bean + @DependsOn({"flyway", "flywayInitializer"}) + public MySQLExecutionDAO mySqlExecutionDAO( + @Qualifier("mysqlRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper, + DataSource dataSource) { + return new MySQLExecutionDAO(retryTemplate, objectMapper, dataSource); + } + + @Bean + @DependsOn({"flyway", "flywayInitializer"}) + public MySQLQueueDAO mySqlQueueDAO( + @Qualifier("mysqlRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper, + DataSource dataSource) { + return new MySQLQueueDAO(retryTemplate, objectMapper, dataSource); + } + + @Bean + public RetryTemplate mysqlRetryTemplate(MySQLProperties properties) { + SimpleRetryPolicy retryPolicy = new CustomRetryPolicy(); + retryPolicy.setMaxAttempts(properties.getDeadlockRetryMax()); + + RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(new NoBackOffPolicy()); + return retryTemplate; + } + + public static class CustomRetryPolicy extends SimpleRetryPolicy { + + @Override + public boolean canRetry(final RetryContext context) { + final Optional lastThrowable = + Optional.ofNullable(context.getLastThrowable()); + return lastThrowable + .map(throwable -> super.canRetry(context) && isDeadLockError(throwable)) + .orElseGet(() -> super.canRetry(context)); + } + + private boolean isDeadLockError(Throwable throwable) { + SQLException sqlException = findCauseSQLException(throwable); + if (sqlException == null) { + return false; + } + return ER_LOCK_DEADLOCK == sqlException.getErrorCode(); + } + + private SQLException findCauseSQLException(Throwable throwable) { + Throwable causeException = throwable; + while (null != causeException && !(causeException instanceof SQLException)) { + causeException = causeException.getCause(); + } + return (SQLException) causeException; + } + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java new file mode 100644 index 000000000..9ed9f1867 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java @@ -0,0 +1,41 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.config; + +import java.time.Duration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("conductor.mysql") +public class MySQLProperties { + + /** The time (in seconds) after which the in-memory task definitions cache will be refreshed */ + private Duration taskDefCacheRefreshInterval = Duration.ofSeconds(60); + + private Integer deadlockRetryMax = 3; + + public Duration getTaskDefCacheRefreshInterval() { + return taskDefCacheRefreshInterval; + } + + public void setTaskDefCacheRefreshInterval(Duration taskDefCacheRefreshInterval) { + this.taskDefCacheRefreshInterval = taskDefCacheRefreshInterval; + } + + public Integer getDeadlockRetryMax() { + return deadlockRetryMax; + } + + public void setDeadlockRetryMax(Integer deadlockRetryMax) { + this.deadlockRetryMax = deadlockRetryMax; + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java new file mode 100644 index 000000000..7e133c72f --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java @@ -0,0 +1,255 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.mysql.util.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; + +public abstract class MySQLBaseDAO { + + private static final List EXCLUDED_STACKTRACE_CLASS = + ImmutableList.of(MySQLBaseDAO.class.getName(), Thread.class.getName()); + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final ObjectMapper objectMapper; + protected final DataSource dataSource; + + private final RetryTemplate retryTemplate; + + protected MySQLBaseDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + this.retryTemplate = retryTemplate; + this.objectMapper = objectMapper; + this.dataSource = dataSource; + } + + protected final LazyToString getCallingMethod() { + return new LazyToString( + () -> + Arrays.stream(Thread.currentThread().getStackTrace()) + .filter( + ste -> + !EXCLUDED_STACKTRACE_CLASS.contains( + ste.getClassName())) + .findFirst() + .map(StackTraceElement::getMethodName) + .orElseThrow(() -> new NullPointerException("Cannot find Caller"))); + } + + protected String toJson(Object value) { + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected T readValue(String json, Class tClass) { + try { + return objectMapper.readValue(json, tClass); + } catch (IOException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected T readValue(String json, TypeReference typeReference) { + try { + return objectMapper.readValue(json, typeReference); + } catch (IOException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Initialize a new transactional {@link Connection} from {@link #dataSource} and pass it to + * {@literal function}. + * + *

Successful executions of {@literal function} will result in a commit and return of {@link + * TransactionalFunction#apply(Connection)}. + * + *

If any {@link Throwable} thrown from {@code TransactionalFunction#apply(Connection)} will + * result in a rollback of the transaction and will be wrapped in an {@link + * NonTransientException} if it is not already one. + * + *

Generally this is used to wrap multiple {@link #execute(Connection, String, + * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that + * produce some expected return value. + * + * @param function The function to apply with a new transactional {@link Connection} + * @param The return type. + * @return The result of {@code TransactionalFunction#apply(Connection)} + * @throws NonTransientException If any errors occur. + */ + private R getWithTransaction(final TransactionalFunction function) { + final Instant start = Instant.now(); + LazyToString callingMethod = getCallingMethod(); + logger.trace("{} : starting transaction", callingMethod); + + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(false); + try { + R result = function.apply(tx); + tx.commit(); + return result; + } catch (Throwable th) { + tx.rollback(); + if (th instanceof NonTransientException) { + throw th; + } + throw new NonTransientException(th.getMessage(), th); + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + logger.trace( + "{} : took {}ms", + callingMethod, + Duration.between(start, Instant.now()).toMillis()); + } + } + + R getWithRetriedTransactions(final TransactionalFunction function) { + try { + return retryTemplate.execute(context -> getWithTransaction(function)); + } catch (Exception e) { + throw new NonTransientException(e.getMessage(), e); + } + } + + protected R getWithTransactionWithOutErrorPropagation(TransactionalFunction function) { + Instant start = Instant.now(); + LazyToString callingMethod = getCallingMethod(); + logger.trace("{} : starting transaction", callingMethod); + + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(false); + try { + R result = function.apply(tx); + tx.commit(); + return result; + } catch (Throwable th) { + tx.rollback(); + logger.info(th.getMessage()); + return null; + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + logger.trace( + "{} : took {}ms", + callingMethod, + Duration.between(start, Instant.now()).toMillis()); + } + } + + /** + * Wraps {@link #getWithRetriedTransactions(TransactionalFunction)} with no return value. + * + *

Generally this is used to wrap multiple {@link #execute(Connection, String, + * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that + * produce no expected return value. + * + * @param consumer The {@link Consumer} callback to pass a transactional {@link Connection} to. + * @throws NonTransientException If any errors occur. + * @see #getWithRetriedTransactions(TransactionalFunction) + */ + protected void withTransaction(Consumer consumer) { + getWithRetriedTransactions( + connection -> { + consumer.accept(connection); + return null; + }); + } + + /** + * Initiate a new transaction and execute a {@link Query} within that context, then return the + * results of {@literal function}. + * + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + * @param The expected return type of {@literal function}. + * @return The results of applying {@literal function}. + */ + protected R queryWithTransaction(String query, QueryFunction function) { + return getWithRetriedTransactions(tx -> query(tx, query, function)); + } + + /** + * Execute a {@link Query} within the context of a given transaction and return the results of + * {@literal function}. + * + * @param tx The transactional {@link Connection} to use. + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + * @param The expected return type of {@literal function}. + * @return The results of applying {@literal function}. + */ + protected R query(Connection tx, String query, QueryFunction function) { + try (Query q = new Query(objectMapper, tx, query)) { + return function.apply(q); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute a statement with no expected return value within a given transaction. + * + * @param tx The transactional {@link Connection} to use. + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + */ + protected void execute(Connection tx, String query, ExecuteFunction function) { + try (Query q = new Query(objectMapper, tx, query)) { + function.apply(q); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Instantiates a new transactional connection and invokes {@link #execute(Connection, String, + * ExecuteFunction)} + * + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + */ + protected void executeWithTransaction(String query, ExecuteFunction function) { + withTransaction(tx -> execute(tx, query, function)); + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java new file mode 100644 index 000000000..5bb94daf3 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java @@ -0,0 +1,1062 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.sql.Connection; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.common.metadata.events.EventExecution; +import com.netflix.conductor.common.metadata.tasks.PollData; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.dao.ConcurrentExecutionLimitDAO; +import com.netflix.conductor.dao.ExecutionDAO; +import com.netflix.conductor.dao.PollDataDAO; +import com.netflix.conductor.dao.RateLimitingDAO; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; +import com.netflix.conductor.mysql.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public class MySQLExecutionDAO extends MySQLBaseDAO + implements ExecutionDAO, RateLimitingDAO, PollDataDAO, ConcurrentExecutionLimitDAO { + + public MySQLExecutionDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + super(retryTemplate, objectMapper, dataSource); + } + + private static String dateStr(Long timeInMs) { + Date date = new Date(timeInMs); + return dateStr(date); + } + + private static String dateStr(Date date) { + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + return format.format(date); + } + + @Override + public List getPendingTasksByWorkflow(String taskDefName, String workflowId) { + // @formatter:off + String GET_IN_PROGRESS_TASKS_FOR_WORKFLOW = + "SELECT json_data FROM task_in_progress tip " + + "INNER JOIN task t ON t.task_id = tip.task_id " + + "WHERE task_def_name = ? AND workflow_id = ?"; + // @formatter:on + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_FOR_WORKFLOW, + q -> + q.addParameter(taskDefName) + .addParameter(workflowId) + .executeAndFetch(TaskModel.class)); + } + + @Override + public List getTasks(String taskDefName, String startKey, int count) { + List tasks = new ArrayList<>(count); + + List pendingTasks = getPendingTasksForTaskType(taskDefName); + boolean startKeyFound = startKey == null; + int found = 0; + for (TaskModel pendingTask : pendingTasks) { + if (!startKeyFound) { + if (pendingTask.getTaskId().equals(startKey)) { + startKeyFound = true; + // noinspection ConstantConditions + if (startKey != null) { + continue; + } + } + } + if (startKeyFound && found < count) { + tasks.add(pendingTask); + found++; + } + } + + return tasks; + } + + private static String taskKey(TaskModel task) { + return task.getReferenceTaskName() + "_" + task.getRetryCount(); + } + + @Override + public List createTasks(List tasks) { + List created = Lists.newArrayListWithCapacity(tasks.size()); + + withTransaction( + connection -> { + for (TaskModel task : tasks) { + validate(task); + + task.setScheduledTime(System.currentTimeMillis()); + + final String taskKey = taskKey(task); + + boolean scheduledTaskAdded = addScheduledTask(connection, task, taskKey); + + if (!scheduledTaskAdded) { + logger.trace( + "Task already scheduled, skipping the run " + + task.getTaskId() + + ", ref=" + + task.getReferenceTaskName() + + ", key=" + + taskKey); + continue; + } + + insertOrUpdateTaskData(connection, task); + addWorkflowToTaskMapping(connection, task); + addTaskInProgress(connection, task); + updateTask(connection, task); + + created.add(task); + } + }); + + return created; + } + + @Override + public void updateTask(TaskModel task) { + withTransaction(connection -> updateTask(connection, task)); + } + + /** + * This is a dummy implementation and this feature is not for Mysql backed Conductor + * + * @param task: which needs to be evaluated whether it is rateLimited or not + */ + @Override + public boolean exceedsRateLimitPerFrequency(TaskModel task, TaskDef taskDef) { + return false; + } + + @Override + public boolean exceedsLimit(TaskModel task) { + + Optional taskDefinition = task.getTaskDefinition(); + if (taskDefinition.isEmpty()) { + return false; + } + + TaskDef taskDef = taskDefinition.get(); + + int limit = taskDef.concurrencyLimit(); + if (limit <= 0) { + return false; + } + + long current = getInProgressTaskCount(task.getTaskDefName()); + + if (current >= limit) { + Monitors.recordTaskConcurrentExecutionLimited(task.getTaskDefName(), limit); + return true; + } + + logger.info( + "Task execution count for {}: limit={}, current={}", + task.getTaskDefName(), + limit, + getInProgressTaskCount(task.getTaskDefName())); + + String taskId = task.getTaskId(); + + List tasksInProgressInOrderOfArrival = + findAllTasksInProgressInOrderOfArrival(task, limit); + + boolean rateLimited = !tasksInProgressInOrderOfArrival.contains(taskId); + + if (rateLimited) { + logger.info( + "Task execution count limited. {}, limit {}, current {}", + task.getTaskDefName(), + limit, + getInProgressTaskCount(task.getTaskDefName())); + Monitors.recordTaskConcurrentExecutionLimited(task.getTaskDefName(), limit); + } + + return rateLimited; + } + + @Override + public boolean removeTask(String taskId) { + TaskModel task = getTask(taskId); + + if (task == null) { + logger.warn("No such task found by id {}", taskId); + return false; + } + + final String taskKey = taskKey(task); + + withTransaction( + connection -> { + removeScheduledTask(connection, task, taskKey); + removeWorkflowToTaskMapping(connection, task); + removeTaskInProgress(connection, task); + removeTaskData(connection, task); + }); + return true; + } + + @Override + public TaskModel getTask(String taskId) { + String GET_TASK = "SELECT json_data FROM task WHERE task_id = ?"; + return queryWithTransaction( + GET_TASK, q -> q.addParameter(taskId).executeAndFetchFirst(TaskModel.class)); + } + + @Override + public List getTasks(List taskIds) { + if (taskIds.isEmpty()) { + return Lists.newArrayList(); + } + return getWithRetriedTransactions(c -> getTasks(c, taskIds)); + } + + @Override + public List getPendingTasksForTaskType(String taskName) { + Preconditions.checkNotNull(taskName, "task name cannot be null"); + // @formatter:off + String GET_IN_PROGRESS_TASKS_FOR_TYPE = + "SELECT json_data FROM task_in_progress tip " + + "INNER JOIN task t ON t.task_id = tip.task_id " + + "WHERE task_def_name = ?"; + // @formatter:on + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_FOR_TYPE, + q -> q.addParameter(taskName).executeAndFetch(TaskModel.class)); + } + + @Override + public List getTasksForWorkflow(String workflowId) { + String GET_TASKS_FOR_WORKFLOW = + "SELECT task_id FROM workflow_to_task WHERE workflow_id = ?"; + return getWithRetriedTransactions( + tx -> + query( + tx, + GET_TASKS_FOR_WORKFLOW, + q -> { + List taskIds = + q.addParameter(workflowId) + .executeScalarList(String.class); + return getTasks(tx, taskIds); + })); + } + + @Override + public String createWorkflow(WorkflowModel workflow) { + return insertOrUpdateWorkflow(workflow, false); + } + + @Override + public String updateWorkflow(WorkflowModel workflow) { + return insertOrUpdateWorkflow(workflow, true); + } + + @Override + public boolean removeWorkflow(String workflowId) { + boolean removed = false; + WorkflowModel workflow = getWorkflow(workflowId, true); + if (workflow != null) { + withTransaction( + connection -> { + removeWorkflowDefToWorkflowMapping(connection, workflow); + removeWorkflow(connection, workflowId); + removePendingWorkflow(connection, workflow.getWorkflowName(), workflowId); + }); + removed = true; + + for (TaskModel task : workflow.getTasks()) { + if (!removeTask(task.getTaskId())) { + removed = false; + } + } + } + return removed; + } + + /** + * This is a dummy implementation and this feature is not supported for MySQL backed Conductor + */ + @Override + public boolean removeWorkflowWithExpiry(String workflowId, int ttlSeconds) { + throw new UnsupportedOperationException( + "This method is not implemented in MySQLExecutionDAO. Please use RedisDAO mode instead for using TTLs."); + } + + @Override + public void removeFromPendingWorkflow(String workflowType, String workflowId) { + withTransaction(connection -> removePendingWorkflow(connection, workflowType, workflowId)); + } + + @Override + public WorkflowModel getWorkflow(String workflowId) { + return getWorkflow(workflowId, true); + } + + @Override + public WorkflowModel getWorkflow(String workflowId, boolean includeTasks) { + WorkflowModel workflow = getWithRetriedTransactions(tx -> readWorkflow(tx, workflowId)); + + if (workflow != null) { + if (includeTasks) { + List tasks = getTasksForWorkflow(workflowId); + tasks.sort(Comparator.comparingInt(TaskModel::getSeq)); + workflow.setTasks(tasks); + } + } + return workflow; + } + + /** + * @param workflowName name of the workflow + * @param version the workflow version + * @return list of workflow ids that are in RUNNING state returns workflows of all versions + * for the given workflow name + */ + @Override + public List getRunningWorkflowIds(String workflowName, int version) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + String GET_PENDING_WORKFLOW_IDS = + "SELECT workflow_id FROM workflow_pending WHERE workflow_type = ?"; + + return queryWithTransaction( + GET_PENDING_WORKFLOW_IDS, + q -> q.addParameter(workflowName).executeScalarList(String.class)); + } + + /** + * @param workflowName Name of the workflow + * @param version the workflow version + * @return list of workflows that are in RUNNING state + */ + @Override + public List getPendingWorkflowsByType(String workflowName, int version) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + return getRunningWorkflowIds(workflowName, version).stream() + .map(this::getWorkflow) + .filter(workflow -> workflow.getWorkflowVersion() == version) + .collect(Collectors.toList()); + } + + @Override + public long getPendingWorkflowCount(String workflowName) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + String GET_PENDING_WORKFLOW_COUNT = + "SELECT COUNT(*) FROM workflow_pending WHERE workflow_type = ?"; + + return queryWithTransaction( + GET_PENDING_WORKFLOW_COUNT, q -> q.addParameter(workflowName).executeCount()); + } + + @Override + public long getInProgressTaskCount(String taskDefName) { + String GET_IN_PROGRESS_TASK_COUNT = + "SELECT COUNT(*) FROM task_in_progress WHERE task_def_name = ? AND in_progress_status = true"; + + return queryWithTransaction( + GET_IN_PROGRESS_TASK_COUNT, q -> q.addParameter(taskDefName).executeCount()); + } + + @Override + public List getWorkflowsByType( + String workflowName, Long startTime, Long endTime) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + Preconditions.checkNotNull(startTime, "startTime cannot be null"); + Preconditions.checkNotNull(endTime, "endTime cannot be null"); + + List workflows = new LinkedList<>(); + + withTransaction( + tx -> { + // @formatter:off + String GET_ALL_WORKFLOWS_FOR_WORKFLOW_DEF = + "SELECT workflow_id FROM workflow_def_to_workflow " + + "WHERE workflow_def = ? AND date_str BETWEEN ? AND ?"; + // @formatter:on + + List workflowIds = + query( + tx, + GET_ALL_WORKFLOWS_FOR_WORKFLOW_DEF, + q -> + q.addParameter(workflowName) + .addParameter(dateStr(startTime)) + .addParameter(dateStr(endTime)) + .executeScalarList(String.class)); + workflowIds.forEach( + workflowId -> { + try { + WorkflowModel wf = getWorkflow(workflowId); + if (wf.getCreateTime() >= startTime + && wf.getCreateTime() <= endTime) { + workflows.add(wf); + } + } catch (Exception e) { + logger.error( + "Unable to load workflow id {} with name {}", + workflowId, + workflowName, + e); + } + }); + }); + + return workflows; + } + + @Override + public List getWorkflowsByCorrelationId( + String workflowName, String correlationId, boolean includeTasks) { + Preconditions.checkNotNull(correlationId, "correlationId cannot be null"); + String GET_WORKFLOWS_BY_CORRELATION_ID = + "SELECT w.json_data FROM workflow w left join workflow_def_to_workflow wd on w.workflow_id = wd.workflow_id WHERE w.correlation_id = ? and wd.workflow_def = ?"; + + return queryWithTransaction( + GET_WORKFLOWS_BY_CORRELATION_ID, + q -> + q.addParameter(correlationId) + .addParameter(workflowName) + .executeAndFetch(WorkflowModel.class)); + } + + @Override + public boolean canSearchAcrossWorkflows() { + return true; + } + + @Override + public boolean addEventExecution(EventExecution eventExecution) { + try { + return getWithRetriedTransactions(tx -> insertEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to add event execution " + eventExecution.getId(), e); + } + } + + @Override + public void removeEventExecution(EventExecution eventExecution) { + try { + withTransaction(tx -> removeEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to remove event execution " + eventExecution.getId(), e); + } + } + + @Override + public void updateEventExecution(EventExecution eventExecution) { + try { + withTransaction(tx -> updateEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to update event execution " + eventExecution.getId(), e); + } + } + + public List getEventExecutions( + String eventHandlerName, String eventName, String messageId, int max) { + try { + List executions = Lists.newLinkedList(); + withTransaction( + tx -> { + for (int i = 0; i < max; i++) { + String executionId = + messageId + "_" + + i; // see SimpleEventProcessor.handle to understand + // how the + // execution id is set + EventExecution ee = + readEventExecution( + tx, + eventHandlerName, + eventName, + messageId, + executionId); + if (ee == null) { + break; + } + executions.add(ee); + } + }); + return executions; + } catch (Exception e) { + String message = + String.format( + "Unable to get event executions for eventHandlerName=%s, eventName=%s, messageId=%s", + eventHandlerName, eventName, messageId); + throw new NonTransientException(message, e); + } + } + + @Override + public void updateLastPollData(String taskDefName, String domain, String workerId) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + PollData pollData = new PollData(taskDefName, domain, workerId, System.currentTimeMillis()); + String effectiveDomain = (domain == null) ? "DEFAULT" : domain; + withTransaction(tx -> insertOrUpdatePollData(tx, pollData, effectiveDomain)); + } + + @Override + public PollData getPollData(String taskDefName, String domain) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + String effectiveDomain = (domain == null) ? "DEFAULT" : domain; + return getWithRetriedTransactions(tx -> readPollData(tx, taskDefName, effectiveDomain)); + } + + @Override + public List getPollData(String taskDefName) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + return readAllPollData(taskDefName); + } + + @Override + public List getAllPollData() { + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(true); + try { + String GET_ALL_POLL_DATA = "SELECT json_data FROM poll_data ORDER BY queue_name"; + return query(tx, GET_ALL_POLL_DATA, q -> q.executeAndFetch(PollData.class)); + } catch (Throwable th) { + throw new NonTransientException(th.getMessage(), th); + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + private List getTasks(Connection connection, List taskIds) { + if (taskIds.isEmpty()) { + return Lists.newArrayList(); + } + + // Generate a formatted query string with a variable number of bind params based + // on taskIds.size() + final String GET_TASKS_FOR_IDS = + String.format( + "SELECT json_data FROM task WHERE task_id IN (%s) AND json_data IS NOT NULL", + Query.generateInBindings(taskIds.size())); + + return query( + connection, + GET_TASKS_FOR_IDS, + q -> q.addParameters(taskIds).executeAndFetch(TaskModel.class)); + } + + private String insertOrUpdateWorkflow(WorkflowModel workflow, boolean update) { + Preconditions.checkNotNull(workflow, "workflow object cannot be null"); + + boolean terminal = workflow.getStatus().isTerminal(); + + List tasks = workflow.getTasks(); + workflow.setTasks(Lists.newLinkedList()); + + withTransaction( + tx -> { + if (!update) { + addWorkflow(tx, workflow); + addWorkflowDefToWorkflowMapping(tx, workflow); + } else { + updateWorkflow(tx, workflow); + } + + if (terminal) { + removePendingWorkflow( + tx, workflow.getWorkflowName(), workflow.getWorkflowId()); + } else { + addPendingWorkflow( + tx, workflow.getWorkflowName(), workflow.getWorkflowId()); + } + }); + + workflow.setTasks(tasks); + return workflow.getWorkflowId(); + } + + private void updateTask(Connection connection, TaskModel task) { + Optional taskDefinition = task.getTaskDefinition(); + + if (taskDefinition.isPresent() && taskDefinition.get().concurrencyLimit() > 0) { + boolean inProgress = + task.getStatus() != null + && task.getStatus().equals(TaskModel.Status.IN_PROGRESS); + updateInProgressStatus(connection, task, inProgress); + } + + insertOrUpdateTaskData(connection, task); + + if (task.getStatus() != null && task.getStatus().isTerminal()) { + removeTaskInProgress(connection, task); + } + + addWorkflowToTaskMapping(connection, task); + } + + private WorkflowModel readWorkflow(Connection connection, String workflowId) { + String GET_WORKFLOW = "SELECT json_data FROM workflow WHERE workflow_id = ?"; + + return query( + connection, + GET_WORKFLOW, + q -> q.addParameter(workflowId).executeAndFetchFirst(WorkflowModel.class)); + } + + private void addWorkflow(Connection connection, WorkflowModel workflow) { + String INSERT_WORKFLOW = + "INSERT INTO workflow (workflow_id, correlation_id, json_data) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowId()) + .addParameter(workflow.getCorrelationId()) + .addJsonParameter(workflow) + .executeUpdate()); + } + + private void updateWorkflow(Connection connection, WorkflowModel workflow) { + String UPDATE_WORKFLOW = + "UPDATE workflow SET json_data = ?, modified_on = CURRENT_TIMESTAMP WHERE workflow_id = ?"; + + execute( + connection, + UPDATE_WORKFLOW, + q -> + q.addJsonParameter(workflow) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + private void removeWorkflow(Connection connection, String workflowId) { + String REMOVE_WORKFLOW = "DELETE FROM workflow WHERE workflow_id = ?"; + execute(connection, REMOVE_WORKFLOW, q -> q.addParameter(workflowId).executeDelete()); + } + + private void addPendingWorkflow(Connection connection, String workflowType, String workflowId) { + + String EXISTS_PENDING_WORKFLOW = + "SELECT EXISTS(SELECT 1 FROM workflow_pending WHERE workflow_type = ? AND workflow_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).exists()); + + if (!exists) { + String INSERT_PENDING_WORKFLOW = + "INSERT IGNORE INTO workflow_pending (workflow_type, workflow_id) VALUES (?, ?)"; + + execute( + connection, + INSERT_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).executeUpdate()); + } + } + + private void removePendingWorkflow( + Connection connection, String workflowType, String workflowId) { + String REMOVE_PENDING_WORKFLOW = + "DELETE FROM workflow_pending WHERE workflow_type = ? AND workflow_id = ?"; + + execute( + connection, + REMOVE_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).executeDelete()); + } + + private void insertOrUpdateTaskData(Connection connection, TaskModel task) { + /* + * Most times the row will be updated so let's try the update first. This used to be an 'INSERT/ON DUPLICATE KEY update' sql statement. The problem with that + * is that if we try the INSERT first, the sequence will be increased even if the ON DUPLICATE KEY happens. + */ + String UPDATE_TASK = + "UPDATE task SET json_data=?, modified_on=CURRENT_TIMESTAMP WHERE task_id=?"; + int rowsUpdated = + query( + connection, + UPDATE_TASK, + q -> + q.addJsonParameter(task) + .addParameter(task.getTaskId()) + .executeUpdate()); + + if (rowsUpdated == 0) { + String INSERT_TASK = + "INSERT INTO task (task_id, json_data, modified_on) VALUES (?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE json_data=VALUES(json_data), modified_on=VALUES(modified_on)"; + execute( + connection, + INSERT_TASK, + q -> q.addParameter(task.getTaskId()).addJsonParameter(task).executeUpdate()); + } + } + + private void removeTaskData(Connection connection, TaskModel task) { + String REMOVE_TASK = "DELETE FROM task WHERE task_id = ?"; + execute(connection, REMOVE_TASK, q -> q.addParameter(task.getTaskId()).executeDelete()); + } + + private void addWorkflowToTaskMapping(Connection connection, TaskModel task) { + + String EXISTS_WORKFLOW_TO_TASK = + "SELECT EXISTS(SELECT 1 FROM workflow_to_task WHERE workflow_id = ? AND task_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .exists()); + + if (!exists) { + String INSERT_WORKFLOW_TO_TASK = + "INSERT IGNORE INTO workflow_to_task (workflow_id, task_id) VALUES (?, ?)"; + + execute( + connection, + INSERT_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + } + + private void removeWorkflowToTaskMapping(Connection connection, TaskModel task) { + String REMOVE_WORKFLOW_TO_TASK = + "DELETE FROM workflow_to_task WHERE workflow_id = ? AND task_id = ?"; + + execute( + connection, + REMOVE_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .executeDelete()); + } + + private void addWorkflowDefToWorkflowMapping(Connection connection, WorkflowModel workflow) { + String INSERT_WORKFLOW_DEF_TO_WORKFLOW = + "INSERT INTO workflow_def_to_workflow (workflow_def, date_str, workflow_id) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_WORKFLOW_DEF_TO_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowName()) + .addParameter(dateStr(workflow.getCreateTime())) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + private void removeWorkflowDefToWorkflowMapping(Connection connection, WorkflowModel workflow) { + String REMOVE_WORKFLOW_DEF_TO_WORKFLOW = + "DELETE FROM workflow_def_to_workflow WHERE workflow_def = ? AND date_str = ? AND workflow_id = ?"; + + execute( + connection, + REMOVE_WORKFLOW_DEF_TO_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowName()) + .addParameter(dateStr(workflow.getCreateTime())) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + @VisibleForTesting + boolean addScheduledTask(Connection connection, TaskModel task, String taskKey) { + + final String EXISTS_SCHEDULED_TASK = + "SELECT EXISTS(SELECT 1 FROM task_scheduled where workflow_id = ? AND task_key = ?)"; + + boolean exists = + query( + connection, + EXISTS_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .exists()); + + if (!exists) { + final String INSERT_IGNORE_SCHEDULED_TASK = + "INSERT IGNORE INTO task_scheduled (workflow_id, task_key, task_id) VALUES (?, ?, ?)"; + + int count = + query( + connection, + INSERT_IGNORE_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .addParameter(task.getTaskId()) + .executeUpdate()); + return count > 0; + } else { + return false; + } + } + + private void removeScheduledTask(Connection connection, TaskModel task, String taskKey) { + String REMOVE_SCHEDULED_TASK = + "DELETE FROM task_scheduled WHERE workflow_id = ? AND task_key = ?"; + execute( + connection, + REMOVE_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .executeDelete()); + } + + private void addTaskInProgress(Connection connection, TaskModel task) { + String EXISTS_IN_PROGRESS_TASK = + "SELECT EXISTS(SELECT 1 FROM task_in_progress WHERE task_def_name = ? AND task_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .exists()); + + if (!exists) { + String INSERT_IN_PROGRESS_TASK = + "INSERT INTO task_in_progress (task_def_name, task_id, workflow_id) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .addParameter(task.getWorkflowInstanceId()) + .executeUpdate()); + } + } + + private void removeTaskInProgress(Connection connection, TaskModel task) { + String REMOVE_IN_PROGRESS_TASK = + "DELETE FROM task_in_progress WHERE task_def_name = ? AND task_id = ?"; + + execute( + connection, + REMOVE_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + + private void updateInProgressStatus(Connection connection, TaskModel task, boolean inProgress) { + String UPDATE_IN_PROGRESS_TASK_STATUS = + "UPDATE task_in_progress SET in_progress_status = ?, modified_on = CURRENT_TIMESTAMP " + + "WHERE task_def_name = ? AND task_id = ?"; + + execute( + connection, + UPDATE_IN_PROGRESS_TASK_STATUS, + q -> + q.addParameter(inProgress) + .addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + + private boolean insertEventExecution(Connection connection, EventExecution eventExecution) { + + String INSERT_EVENT_EXECUTION = + "INSERT INTO event_execution (event_handler_name, event_name, message_id, execution_id, json_data) " + + "VALUES (?, ?, ?, ?, ?)"; + int count = + query( + connection, + INSERT_EVENT_EXECUTION, + q -> + q.addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .addJsonParameter(eventExecution) + .executeUpdate()); + return count > 0; + } + + private void updateEventExecution(Connection connection, EventExecution eventExecution) { + // @formatter:off + String UPDATE_EVENT_EXECUTION = + "UPDATE event_execution SET " + + "json_data = ?, " + + "modified_on = CURRENT_TIMESTAMP " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + // @formatter:on + + execute( + connection, + UPDATE_EVENT_EXECUTION, + q -> + q.addJsonParameter(eventExecution) + .addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .executeUpdate()); + } + + private void removeEventExecution(Connection connection, EventExecution eventExecution) { + String REMOVE_EVENT_EXECUTION = + "DELETE FROM event_execution " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + + execute( + connection, + REMOVE_EVENT_EXECUTION, + q -> + q.addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .executeUpdate()); + } + + private EventExecution readEventExecution( + Connection connection, + String eventHandlerName, + String eventName, + String messageId, + String executionId) { + // @formatter:off + String GET_EVENT_EXECUTION = + "SELECT json_data FROM event_execution " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + // @formatter:on + return query( + connection, + GET_EVENT_EXECUTION, + q -> + q.addParameter(eventHandlerName) + .addParameter(eventName) + .addParameter(messageId) + .addParameter(executionId) + .executeAndFetchFirst(EventExecution.class)); + } + + private void insertOrUpdatePollData(Connection connection, PollData pollData, String domain) { + + /* + * Most times the row will be updated so let's try the update first. This used to be an 'INSERT/ON DUPLICATE KEY update' sql statement. The problem with that + * is that if we try the INSERT first, the sequence will be increased even if the ON DUPLICATE KEY happens. Since polling happens *a lot*, the sequence can increase + * dramatically even though it won't be used. + */ + String UPDATE_POLL_DATA = + "UPDATE poll_data SET json_data=?, modified_on=CURRENT_TIMESTAMP WHERE queue_name=? AND domain=?"; + int rowsUpdated = + query( + connection, + UPDATE_POLL_DATA, + q -> + q.addJsonParameter(pollData) + .addParameter(pollData.getQueueName()) + .addParameter(domain) + .executeUpdate()); + + if (rowsUpdated == 0) { + String INSERT_POLL_DATA = + "INSERT INTO poll_data (queue_name, domain, json_data, modified_on) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE json_data=VALUES(json_data), modified_on=VALUES(modified_on)"; + execute( + connection, + INSERT_POLL_DATA, + q -> + q.addParameter(pollData.getQueueName()) + .addParameter(domain) + .addJsonParameter(pollData) + .executeUpdate()); + } + } + + private PollData readPollData(Connection connection, String queueName, String domain) { + String GET_POLL_DATA = + "SELECT json_data FROM poll_data WHERE queue_name = ? AND domain = ?"; + return query( + connection, + GET_POLL_DATA, + q -> + q.addParameter(queueName) + .addParameter(domain) + .executeAndFetchFirst(PollData.class)); + } + + private List readAllPollData(String queueName) { + String GET_ALL_POLL_DATA = "SELECT json_data FROM poll_data WHERE queue_name = ?"; + return queryWithTransaction( + GET_ALL_POLL_DATA, q -> q.addParameter(queueName).executeAndFetch(PollData.class)); + } + + private List findAllTasksInProgressInOrderOfArrival(TaskModel task, int limit) { + String GET_IN_PROGRESS_TASKS_WITH_LIMIT = + "SELECT task_id FROM task_in_progress WHERE task_def_name = ? ORDER BY created_on LIMIT ?"; + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_WITH_LIMIT, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(limit) + .executeScalarList(String.class)); + } + + private void validate(TaskModel task) { + Preconditions.checkNotNull(task, "task object cannot be null"); + Preconditions.checkNotNull(task.getTaskId(), "Task id cannot be null"); + Preconditions.checkNotNull( + task.getWorkflowInstanceId(), "Workflow instance id cannot be null"); + Preconditions.checkNotNull( + task.getReferenceTaskName(), "Task reference name cannot be null"); + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java new file mode 100644 index 000000000..d04046db1 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java @@ -0,0 +1,560 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import javax.sql.DataSource; + +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.core.exception.ConflictException; +import com.netflix.conductor.core.exception.NotFoundException; +import com.netflix.conductor.dao.EventHandlerDAO; +import com.netflix.conductor.dao.MetadataDAO; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.mysql.config.MySQLProperties; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; + +public class MySQLMetadataDAO extends MySQLBaseDAO implements MetadataDAO, EventHandlerDAO { + + private final ConcurrentHashMap taskDefCache = new ConcurrentHashMap<>(); + private static final String CLASS_NAME = MySQLMetadataDAO.class.getSimpleName(); + + public MySQLMetadataDAO( + RetryTemplate retryTemplate, + ObjectMapper objectMapper, + DataSource dataSource, + MySQLProperties properties) { + super(retryTemplate, objectMapper, dataSource); + + long cacheRefreshTime = properties.getTaskDefCacheRefreshInterval().getSeconds(); + Executors.newSingleThreadScheduledExecutor() + .scheduleWithFixedDelay( + this::refreshTaskDefs, + cacheRefreshTime, + cacheRefreshTime, + TimeUnit.SECONDS); + } + + @Override + public TaskDef createTaskDef(TaskDef taskDef) { + validate(taskDef); + insertOrUpdateTaskDef(taskDef); + return taskDef; + } + + @Override + public TaskDef updateTaskDef(TaskDef taskDef) { + validate(taskDef); + insertOrUpdateTaskDef(taskDef); + return taskDef; + } + + @Override + public TaskDef getTaskDef(String name) { + Preconditions.checkNotNull(name, "TaskDef name cannot be null"); + TaskDef taskDef = taskDefCache.get(name); + if (taskDef == null) { + if (logger.isTraceEnabled()) { + logger.trace("Cache miss: {}", name); + } + taskDef = getTaskDefFromDB(name); + } + + return taskDef; + } + + @Override + public List getAllTaskDefs() { + return getWithRetriedTransactions(this::findAllTaskDefs); + } + + @Override + public void removeTaskDef(String name) { + final String DELETE_TASKDEF_QUERY = "DELETE FROM meta_task_def WHERE name = ?"; + + executeWithTransaction( + DELETE_TASKDEF_QUERY, + q -> { + if (!q.addParameter(name).executeDelete()) { + throw new NotFoundException("No such task definition"); + } + + taskDefCache.remove(name); + }); + } + + @Override + public void createWorkflowDef(WorkflowDef def) { + validate(def); + + withTransaction( + tx -> { + if (workflowExists(tx, def)) { + throw new ConflictException( + "Workflow with " + def.key() + " already exists!"); + } + + insertOrUpdateWorkflowDef(tx, def); + }); + } + + @Override + public void updateWorkflowDef(WorkflowDef def) { + validate(def); + withTransaction(tx -> insertOrUpdateWorkflowDef(tx, def)); + } + + @Override + public Optional getLatestWorkflowDef(String name) { + final String GET_LATEST_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE NAME = ? AND " + + "version = latest_version"; + + return Optional.ofNullable( + queryWithTransaction( + GET_LATEST_WORKFLOW_DEF_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(WorkflowDef.class))); + } + + @Override + public Optional getWorkflowDef(String name, int version) { + final String GET_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE NAME = ? AND version = ?"; + return Optional.ofNullable( + queryWithTransaction( + GET_WORKFLOW_DEF_QUERY, + q -> + q.addParameter(name) + .addParameter(version) + .executeAndFetchFirst(WorkflowDef.class))); + } + + @Override + public void removeWorkflowDef(String name, Integer version) { + final String DELETE_WORKFLOW_QUERY = + "DELETE from meta_workflow_def WHERE name = ? AND version = ?"; + + withTransaction( + tx -> { + // remove specified workflow + execute( + tx, + DELETE_WORKFLOW_QUERY, + q -> { + if (!q.addParameter(name).addParameter(version).executeDelete()) { + throw new NotFoundException( + String.format( + "No such workflow definition: %s version: %d", + name, version)); + } + }); + // reset latest version based on remaining rows for this workflow + Optional maxVersion = getLatestVersion(tx, name); + maxVersion.ifPresent(newVersion -> updateLatestVersion(tx, name, newVersion)); + }); + } + + public List findAll() { + final String FIND_ALL_WORKFLOW_DEF_QUERY = "SELECT DISTINCT name FROM meta_workflow_def"; + return queryWithTransaction( + FIND_ALL_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(String.class)); + } + + @Override + public List getAllWorkflowDefs() { + final String GET_ALL_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def ORDER BY name, version"; + + return queryWithTransaction( + GET_ALL_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(WorkflowDef.class)); + } + + @Override + public List getAllWorkflowDefsLatestVersions() { + final String GET_ALL_WORKFLOW_DEF_LATEST_VERSIONS_QUERY = + "SELECT json_data FROM meta_workflow_def wd WHERE wd.version = (SELECT MAX(version) FROM meta_workflow_def wd2 WHERE wd2.name = wd.name)"; + return queryWithTransaction( + GET_ALL_WORKFLOW_DEF_LATEST_VERSIONS_QUERY, + q -> q.executeAndFetch(WorkflowDef.class)); + } + + public List getAllLatest() { + final String GET_ALL_LATEST_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE version = " + "latest_version"; + + return queryWithTransaction( + GET_ALL_LATEST_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(WorkflowDef.class)); + } + + public List getAllVersions(String name) { + final String GET_ALL_VERSIONS_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE name = ? " + "ORDER BY version"; + + return queryWithTransaction( + GET_ALL_VERSIONS_WORKFLOW_DEF_QUERY, + q -> q.addParameter(name).executeAndFetch(WorkflowDef.class)); + } + + @Override + public void addEventHandler(EventHandler eventHandler) { + Preconditions.checkNotNull(eventHandler.getName(), "EventHandler name cannot be null"); + + final String INSERT_EVENT_HANDLER_QUERY = + "INSERT INTO meta_event_handler (name, event, active, json_data) " + + "VALUES (?, ?, ?, ?)"; + + withTransaction( + tx -> { + if (getEventHandler(tx, eventHandler.getName()) != null) { + throw new ConflictException( + "EventHandler with name " + + eventHandler.getName() + + " already exists!"); + } + + execute( + tx, + INSERT_EVENT_HANDLER_QUERY, + q -> + q.addParameter(eventHandler.getName()) + .addParameter(eventHandler.getEvent()) + .addParameter(eventHandler.isActive()) + .addJsonParameter(eventHandler) + .executeUpdate()); + }); + } + + @Override + public void updateEventHandler(EventHandler eventHandler) { + Preconditions.checkNotNull(eventHandler.getName(), "EventHandler name cannot be null"); + + // @formatter:off + final String UPDATE_EVENT_HANDLER_QUERY = + "UPDATE meta_event_handler SET " + + "event = ?, active = ?, json_data = ?, " + + "modified_on = CURRENT_TIMESTAMP WHERE name = ?"; + // @formatter:on + + withTransaction( + tx -> { + EventHandler existing = getEventHandler(tx, eventHandler.getName()); + if (existing == null) { + throw new NotFoundException( + "EventHandler with name " + eventHandler.getName() + " not found!"); + } + + execute( + tx, + UPDATE_EVENT_HANDLER_QUERY, + q -> + q.addParameter(eventHandler.getEvent()) + .addParameter(eventHandler.isActive()) + .addJsonParameter(eventHandler) + .addParameter(eventHandler.getName()) + .executeUpdate()); + }); + } + + @Override + public void removeEventHandler(String name) { + final String DELETE_EVENT_HANDLER_QUERY = "DELETE FROM meta_event_handler WHERE name = ?"; + + withTransaction( + tx -> { + EventHandler existing = getEventHandler(tx, name); + if (existing == null) { + throw new NotFoundException( + "EventHandler with name " + name + " not found!"); + } + + execute( + tx, + DELETE_EVENT_HANDLER_QUERY, + q -> q.addParameter(name).executeDelete()); + }); + } + + @Override + public List getAllEventHandlers() { + final String READ_ALL_EVENT_HANDLER_QUERY = "SELECT json_data FROM meta_event_handler"; + return queryWithTransaction( + READ_ALL_EVENT_HANDLER_QUERY, q -> q.executeAndFetch(EventHandler.class)); + } + + @Override + public List getEventHandlersForEvent(String event, boolean activeOnly) { + final String READ_ALL_EVENT_HANDLER_BY_EVENT_QUERY = + "SELECT json_data FROM meta_event_handler WHERE event = ?"; + return queryWithTransaction( + READ_ALL_EVENT_HANDLER_BY_EVENT_QUERY, + q -> { + q.addParameter(event); + return q.executeAndFetch( + rs -> { + List handlers = new ArrayList<>(); + while (rs.next()) { + EventHandler h = readValue(rs.getString(1), EventHandler.class); + if (!activeOnly || h.isActive()) { + handlers.add(h); + } + } + + return handlers; + }); + }); + } + + /** + * Use {@link Preconditions} to check for required {@link TaskDef} fields, throwing a Runtime + * exception if validations fail. + * + * @param taskDef The {@code TaskDef} to check. + */ + private void validate(TaskDef taskDef) { + Preconditions.checkNotNull(taskDef, "TaskDef object cannot be null"); + Preconditions.checkNotNull(taskDef.getName(), "TaskDef name cannot be null"); + } + + /** + * Use {@link Preconditions} to check for required {@link WorkflowDef} fields, throwing a + * Runtime exception if validations fail. + * + * @param def The {@code WorkflowDef} to check. + */ + private void validate(WorkflowDef def) { + Preconditions.checkNotNull(def, "WorkflowDef object cannot be null"); + Preconditions.checkNotNull(def.getName(), "WorkflowDef name cannot be null"); + } + + /** + * Retrieve a {@link EventHandler} by {@literal name}. + * + * @param connection The {@link Connection} to use for queries. + * @param name The {@code EventHandler} name to look for. + * @return {@literal null} if nothing is found, otherwise the {@code EventHandler}. + */ + private EventHandler getEventHandler(Connection connection, String name) { + final String READ_ONE_EVENT_HANDLER_QUERY = + "SELECT json_data FROM meta_event_handler WHERE name = ?"; + + return query( + connection, + READ_ONE_EVENT_HANDLER_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(EventHandler.class)); + } + + /** + * Check if a {@link WorkflowDef} with the same {@literal name} and {@literal version} already + * exist. + * + * @param connection The {@link Connection} to use for queries. + * @param def The {@code WorkflowDef} to check for. + * @return {@literal true} if a {@code WorkflowDef} already exists with the same values. + */ + private Boolean workflowExists(Connection connection, WorkflowDef def) { + final String CHECK_WORKFLOW_DEF_EXISTS_QUERY = + "SELECT COUNT(*) FROM meta_workflow_def WHERE name = ? AND " + "version = ?"; + + return query( + connection, + CHECK_WORKFLOW_DEF_EXISTS_QUERY, + q -> q.addParameter(def.getName()).addParameter(def.getVersion()).exists()); + } + + /** + * Return the latest version that exists for the provided {@code name}. + * + * @param tx The {@link Connection} to use for queries. + * @param name The {@code name} to check for. + * @return {@code Optional.empty()} if no versions exist, otherwise the max {@link + * WorkflowDef#getVersion} found. + */ + private Optional getLatestVersion(Connection tx, String name) { + final String GET_LATEST_WORKFLOW_DEF_VERSION = + "SELECT max(version) AS version FROM meta_workflow_def WHERE " + "name = ?"; + + Integer val = + query( + tx, + GET_LATEST_WORKFLOW_DEF_VERSION, + q -> { + q.addParameter(name); + return q.executeAndFetch( + rs -> { + if (!rs.next()) { + return null; + } + + return rs.getInt(1); + }); + }); + + return Optional.ofNullable(val); + } + + /** + * Update the latest version for the workflow with name {@code WorkflowDef} to the version + * provided in {@literal version}. + * + * @param tx The {@link Connection} to use for queries. + * @param name Workflow def name to update + * @param version The new latest {@code version} value. + */ + private void updateLatestVersion(Connection tx, String name, int version) { + final String UPDATE_WORKFLOW_DEF_LATEST_VERSION_QUERY = + "UPDATE meta_workflow_def SET latest_version = ? " + "WHERE name = ?"; + + execute( + tx, + UPDATE_WORKFLOW_DEF_LATEST_VERSION_QUERY, + q -> q.addParameter(version).addParameter(name).executeUpdate()); + } + + private void insertOrUpdateWorkflowDef(Connection tx, WorkflowDef def) { + final String INSERT_WORKFLOW_DEF_QUERY = + "INSERT INTO meta_workflow_def (name, version, json_data) VALUES (?," + " ?, ?)"; + + Optional version = getLatestVersion(tx, def.getName()); + if (!workflowExists(tx, def)) { + execute( + tx, + INSERT_WORKFLOW_DEF_QUERY, + q -> + q.addParameter(def.getName()) + .addParameter(def.getVersion()) + .addJsonParameter(def) + .executeUpdate()); + } else { + // @formatter:off + final String UPDATE_WORKFLOW_DEF_QUERY = + "UPDATE meta_workflow_def " + + "SET json_data = ?, modified_on = CURRENT_TIMESTAMP " + + "WHERE name = ? AND version = ?"; + // @formatter:on + + execute( + tx, + UPDATE_WORKFLOW_DEF_QUERY, + q -> + q.addJsonParameter(def) + .addParameter(def.getName()) + .addParameter(def.getVersion()) + .executeUpdate()); + } + int maxVersion = def.getVersion(); + if (version.isPresent() && version.get() > def.getVersion()) { + maxVersion = version.get(); + } + + updateLatestVersion(tx, def.getName(), maxVersion); + } + + /** + * Query persistence for all defined {@link TaskDef} data, and cache it in {@link + * #taskDefCache}. + */ + private void refreshTaskDefs() { + try { + withTransaction( + tx -> { + Map map = new HashMap<>(); + findAllTaskDefs(tx).forEach(taskDef -> map.put(taskDef.getName(), taskDef)); + + synchronized (taskDefCache) { + taskDefCache.clear(); + taskDefCache.putAll(map); + } + + if (logger.isTraceEnabled()) { + logger.trace("Refreshed {} TaskDefs", taskDefCache.size()); + } + }); + } catch (Exception e) { + Monitors.error(CLASS_NAME, "refreshTaskDefs"); + logger.error("refresh TaskDefs failed ", e); + } + } + + /** + * Query persistence for all defined {@link TaskDef} data. + * + * @param tx The {@link Connection} to use for queries. + * @return A new {@code List} with all the {@code TaskDef} data that was retrieved. + */ + private List findAllTaskDefs(Connection tx) { + final String READ_ALL_TASKDEF_QUERY = "SELECT json_data FROM meta_task_def"; + + return query(tx, READ_ALL_TASKDEF_QUERY, q -> q.executeAndFetch(TaskDef.class)); + } + + /** + * Explicitly retrieves a {@link TaskDef} from persistence, avoiding {@link #taskDefCache}. + * + * @param name The name of the {@code TaskDef} to query for. + * @return {@literal null} if nothing is found, otherwise the {@code TaskDef}. + */ + private TaskDef getTaskDefFromDB(String name) { + final String READ_ONE_TASKDEF_QUERY = "SELECT json_data FROM meta_task_def WHERE name = ?"; + + return queryWithTransaction( + READ_ONE_TASKDEF_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(TaskDef.class)); + } + + private String insertOrUpdateTaskDef(TaskDef taskDef) { + final String UPDATE_TASKDEF_QUERY = + "UPDATE meta_task_def SET json_data = ?, modified_on = CURRENT_TIMESTAMP WHERE name = ?"; + + final String INSERT_TASKDEF_QUERY = + "INSERT INTO meta_task_def (name, json_data) VALUES (?, ?)"; + + return getWithRetriedTransactions( + tx -> { + execute( + tx, + UPDATE_TASKDEF_QUERY, + update -> { + int result = + update.addJsonParameter(taskDef) + .addParameter(taskDef.getName()) + .executeUpdate(); + if (result == 0) { + execute( + tx, + INSERT_TASKDEF_QUERY, + insert -> + insert.addParameter(taskDef.getName()) + .addJsonParameter(taskDef) + .executeUpdate()); + } + }); + + taskDefCache.put(taskDef.getName(), taskDef); + return taskDef.getName(); + }); + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java new file mode 100644 index 000000000..ffbaf506f --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java @@ -0,0 +1,394 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.dao.QueueDAO; +import com.netflix.conductor.mysql.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.Uninterruptibles; + +public class MySQLQueueDAO extends MySQLBaseDAO implements QueueDAO { + + private static final Long UNACK_SCHEDULE_MS = 60_000L; + + public MySQLQueueDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + super(retryTemplate, objectMapper, dataSource); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + this::processAllUnacks, + UNACK_SCHEDULE_MS, + UNACK_SCHEDULE_MS, + TimeUnit.MILLISECONDS); + logger.debug(MySQLQueueDAO.class.getName() + " is ready to serve"); + } + + @Override + public void push(String queueName, String messageId, long offsetTimeInSecond) { + push(queueName, messageId, 0, offsetTimeInSecond); + } + + @Override + public void push(String queueName, String messageId, int priority, long offsetTimeInSecond) { + withTransaction( + tx -> pushMessage(tx, queueName, messageId, null, priority, offsetTimeInSecond)); + } + + @Override + public void push(String queueName, List messages) { + withTransaction( + tx -> + messages.forEach( + message -> + pushMessage( + tx, + queueName, + message.getId(), + message.getPayload(), + message.getPriority(), + 0))); + } + + @Override + public boolean pushIfNotExists(String queueName, String messageId, long offsetTimeInSecond) { + return pushIfNotExists(queueName, messageId, 0, offsetTimeInSecond); + } + + @Override + public boolean pushIfNotExists( + String queueName, String messageId, int priority, long offsetTimeInSecond) { + return getWithRetriedTransactions( + tx -> { + if (!existsMessage(tx, queueName, messageId)) { + pushMessage(tx, queueName, messageId, null, priority, offsetTimeInSecond); + return true; + } + return false; + }); + } + + @Override + public List pop(String queueName, int count, int timeout) { + List messages = + getWithTransactionWithOutErrorPropagation( + tx -> popMessages(tx, queueName, count, timeout)); + if (messages == null) { + return new ArrayList<>(); + } + return messages.stream().map(Message::getId).collect(Collectors.toList()); + } + + @Override + public List pollMessages(String queueName, int count, int timeout) { + List messages = + getWithTransactionWithOutErrorPropagation( + tx -> popMessages(tx, queueName, count, timeout)); + if (messages == null) { + return new ArrayList<>(); + } + return messages; + } + + @Override + public void remove(String queueName, String messageId) { + withTransaction(tx -> removeMessage(tx, queueName, messageId)); + } + + @Override + public int getSize(String queueName) { + final String GET_QUEUE_SIZE = "SELECT COUNT(*) FROM queue_message WHERE queue_name = ?"; + return queryWithTransaction( + GET_QUEUE_SIZE, q -> ((Long) q.addParameter(queueName).executeCount()).intValue()); + } + + @Override + public boolean ack(String queueName, String messageId) { + return getWithRetriedTransactions(tx -> removeMessage(tx, queueName, messageId)); + } + + @Override + public boolean setUnackTimeout(String queueName, String messageId, long unackTimeout) { + long updatedOffsetTimeInSecond = unackTimeout / 1000; + + final String UPDATE_UNACK_TIMEOUT = + "UPDATE queue_message SET offset_time_seconds = ?, deliver_on = TIMESTAMPADD(SECOND, ?, CURRENT_TIMESTAMP) WHERE queue_name = ? AND message_id = ?"; + + return queryWithTransaction( + UPDATE_UNACK_TIMEOUT, + q -> + q.addParameter(updatedOffsetTimeInSecond) + .addParameter(updatedOffsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate()) + == 1; + } + + @Override + public void flush(String queueName) { + final String FLUSH_QUEUE = "DELETE FROM queue_message WHERE queue_name = ?"; + executeWithTransaction(FLUSH_QUEUE, q -> q.addParameter(queueName).executeDelete()); + } + + @Override + public Map queuesDetail() { + final String GET_QUEUES_DETAIL = + "SELECT queue_name, (SELECT count(*) FROM queue_message WHERE popped = false AND queue_name = q.queue_name) AS size FROM queue q"; + return queryWithTransaction( + GET_QUEUES_DETAIL, + q -> + q.executeAndFetch( + rs -> { + Map detail = Maps.newHashMap(); + while (rs.next()) { + String queueName = rs.getString("queue_name"); + Long size = rs.getLong("size"); + detail.put(queueName, size); + } + return detail; + })); + } + + @Override + public Map>> queuesDetailVerbose() { + // @formatter:off + final String GET_QUEUES_DETAIL_VERBOSE = + "SELECT queue_name, \n" + + " (SELECT count(*) FROM queue_message WHERE popped = false AND queue_name = q.queue_name) AS size,\n" + + " (SELECT count(*) FROM queue_message WHERE popped = true AND queue_name = q.queue_name) AS uacked \n" + + "FROM queue q"; + // @formatter:on + + return queryWithTransaction( + GET_QUEUES_DETAIL_VERBOSE, + q -> + q.executeAndFetch( + rs -> { + Map>> result = + Maps.newHashMap(); + while (rs.next()) { + String queueName = rs.getString("queue_name"); + Long size = rs.getLong("size"); + Long queueUnacked = rs.getLong("uacked"); + result.put( + queueName, + ImmutableMap.of( + "a", + ImmutableMap + .of( // sharding not implemented, + // returning only + // one shard with all the + // info + "size", + size, + "uacked", + queueUnacked))); + } + return result; + })); + } + + /** + * Un-pop all un-acknowledged messages for all queues. + * + * @since 1.11.6 + */ + public void processAllUnacks() { + + logger.trace("processAllUnacks started"); + + final String PROCESS_ALL_UNACKS = + "UPDATE queue_message SET popped = false WHERE popped = true AND TIMESTAMPADD(SECOND,-60,CURRENT_TIMESTAMP) > deliver_on"; + executeWithTransaction(PROCESS_ALL_UNACKS, Query::executeUpdate); + } + + @Override + public void processUnacks(String queueName) { + final String PROCESS_UNACKS = + "UPDATE queue_message SET popped = false WHERE queue_name = ? AND popped = true AND TIMESTAMPADD(SECOND,-60,CURRENT_TIMESTAMP) > deliver_on"; + executeWithTransaction(PROCESS_UNACKS, q -> q.addParameter(queueName).executeUpdate()); + } + + @Override + public boolean resetOffsetTime(String queueName, String messageId) { + long offsetTimeInSecond = 0; // Reset to 0 + final String SET_OFFSET_TIME = + "UPDATE queue_message SET offset_time_seconds = ?, deliver_on = TIMESTAMPADD(SECOND,?,CURRENT_TIMESTAMP) \n" + + "WHERE queue_name = ? AND message_id = ?"; + + return queryWithTransaction( + SET_OFFSET_TIME, + q -> + q.addParameter(offsetTimeInSecond) + .addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate() + == 1); + } + + private boolean existsMessage(Connection connection, String queueName, String messageId) { + final String EXISTS_MESSAGE = + "SELECT EXISTS(SELECT 1 FROM queue_message WHERE queue_name = ? AND message_id = ?)"; + return query( + connection, + EXISTS_MESSAGE, + q -> q.addParameter(queueName).addParameter(messageId).exists()); + } + + private void pushMessage( + Connection connection, + String queueName, + String messageId, + String payload, + Integer priority, + long offsetTimeInSecond) { + + createQueueIfNotExists(connection, queueName); + + String UPDATE_MESSAGE = + "UPDATE queue_message SET payload=?, deliver_on=TIMESTAMPADD(SECOND,?,CURRENT_TIMESTAMP) WHERE queue_name = ? AND message_id = ?"; + int rowsUpdated = + query( + connection, + UPDATE_MESSAGE, + q -> + q.addParameter(payload) + .addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate()); + + if (rowsUpdated == 0) { + String PUSH_MESSAGE = + "INSERT INTO queue_message (deliver_on, queue_name, message_id, priority, offset_time_seconds, payload) VALUES (TIMESTAMPADD(SECOND,?,CURRENT_TIMESTAMP), ?, ?,?,?,?) ON DUPLICATE KEY UPDATE payload=VALUES(payload), deliver_on=VALUES(deliver_on)"; + execute( + connection, + PUSH_MESSAGE, + q -> + q.addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .addParameter(priority) + .addParameter(offsetTimeInSecond) + .addParameter(payload) + .executeUpdate()); + } + } + + private boolean removeMessage(Connection connection, String queueName, String messageId) { + final String REMOVE_MESSAGE = + "DELETE FROM queue_message WHERE queue_name = ? AND message_id = ?"; + return query( + connection, + REMOVE_MESSAGE, + q -> q.addParameter(queueName).addParameter(messageId).executeDelete()); + } + + private List peekMessages(Connection connection, String queueName, int count) { + if (count < 1) { + return Collections.emptyList(); + } + + final String PEEK_MESSAGES = + "SELECT message_id, priority, payload FROM queue_message use index(combo_queue_message) WHERE queue_name = ? AND popped = false AND deliver_on <= TIMESTAMPADD(MICROSECOND, 1000, CURRENT_TIMESTAMP) ORDER BY priority DESC, deliver_on, created_on LIMIT ?"; + + return query( + connection, + PEEK_MESSAGES, + p -> + p.addParameter(queueName) + .addParameter(count) + .executeAndFetch( + rs -> { + List results = new ArrayList<>(); + while (rs.next()) { + Message m = new Message(); + m.setId(rs.getString("message_id")); + m.setPriority(rs.getInt("priority")); + m.setPayload(rs.getString("payload")); + results.add(m); + } + return results; + })); + } + + private List popMessages( + Connection connection, String queueName, int count, int timeout) { + long start = System.currentTimeMillis(); + List messages = peekMessages(connection, queueName, count); + + while (messages.size() < count && ((System.currentTimeMillis() - start) < timeout)) { + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + messages = peekMessages(connection, queueName, count); + } + + if (messages.isEmpty()) { + return messages; + } + + List poppedMessages = new ArrayList<>(); + for (Message message : messages) { + final String POP_MESSAGE = + "UPDATE queue_message SET popped = true WHERE queue_name = ? AND message_id = ? AND popped = false"; + int result = + query( + connection, + POP_MESSAGE, + q -> + q.addParameter(queueName) + .addParameter(message.getId()) + .executeUpdate()); + + if (result == 1) { + poppedMessages.add(message); + } + } + return poppedMessages; + } + + private void createQueueIfNotExists(Connection connection, String queueName) { + logger.trace("Creating new queue '{}'", queueName); + final String EXISTS_QUEUE = "SELECT EXISTS(SELECT 1 FROM queue WHERE queue_name = ?)"; + boolean exists = query(connection, EXISTS_QUEUE, q -> q.addParameter(queueName).exists()); + if (!exists) { + final String CREATE_QUEUE = "INSERT IGNORE INTO queue (queue_name) VALUES (?)"; + execute(connection, CREATE_QUEUE, q -> q.addParameter(queueName).executeUpdate()); + } + } + + @Override + public boolean containsMessage(String queueName, String messageId) { + final String EXISTS_QUEUE = + "SELECT EXISTS(SELECT 1 FROM queue_message WHERE queue_name = ? AND message_id = ? )"; + return queryWithTransaction( + EXISTS_QUEUE, q -> q.addParameter(queueName).addParameter(messageId).exists()); + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java new file mode 100644 index 000000000..44b51f2e9 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java @@ -0,0 +1,25 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.sql.SQLException; + +/** + * Functional interface for {@link Query} executions with no expected result. + * + * @author mustafa + */ +@FunctionalInterface +public interface ExecuteFunction { + + void apply(Query query) throws SQLException; +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java new file mode 100644 index 000000000..41f83ff35 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java @@ -0,0 +1,32 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.util.function.Supplier; + +/** Functional class to support the lazy execution of a String result. */ +public class LazyToString { + + private final Supplier supplier; + + /** + * @param supplier Supplier to execute when {@link #toString()} is called. + */ + public LazyToString(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public String toString() { + return supplier.get(); + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java new file mode 100644 index 000000000..1df27602c --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java @@ -0,0 +1,619 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.exception.NonTransientException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Represents a {@link PreparedStatement} that is wrapped with convenience methods and utilities. + * + *

This class simulates a parameter building pattern and all {@literal addParameter(*)} methods + * must be called in the proper order of their expected binding sequence. + * + * @author mustafa + */ +public class Query implements AutoCloseable { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The {@link ObjectMapper} instance to use for serializing/deserializing JSON. */ + protected final ObjectMapper objectMapper; + + /** The initial supplied query String that was used to prepare {@link #statement}. */ + private final String rawQuery; + + /** + * Parameter index for the {@code ResultSet#set*(*)} methods, gets incremented every time a + * parameter is added to the {@code PreparedStatement} {@link #statement}. + */ + private final AtomicInteger index = new AtomicInteger(1); + + /** The {@link PreparedStatement} that will be managed and executed by this class. */ + private final PreparedStatement statement; + + public Query(ObjectMapper objectMapper, Connection connection, String query) { + this.rawQuery = query; + this.objectMapper = objectMapper; + + try { + this.statement = connection.prepareStatement(query); + } catch (SQLException ex) { + throw new NonTransientException( + "Cannot prepare statement for query: " + ex.getMessage(), ex); + } + } + + /** + * Generate a String with {@literal count} number of '?' placeholders for {@link + * PreparedStatement} queries. + * + * @param count The number of '?' chars to generate. + * @return a comma delimited string of {@literal count} '?' binding placeholders. + */ + public static String generateInBindings(int count) { + String[] questions = new String[count]; + for (int i = 0; i < count; i++) { + questions[i] = "?"; + } + + return String.join(", ", questions); + } + + public Query addParameter(final String value) { + return addParameterInternal((ps, idx) -> ps.setString(idx, value)); + } + + public Query addParameter(final int value) { + return addParameterInternal((ps, idx) -> ps.setInt(idx, value)); + } + + public Query addParameter(final boolean value) { + return addParameterInternal(((ps, idx) -> ps.setBoolean(idx, value))); + } + + public Query addParameter(final long value) { + return addParameterInternal((ps, idx) -> ps.setLong(idx, value)); + } + + public Query addParameter(final double value) { + return addParameterInternal((ps, idx) -> ps.setDouble(idx, value)); + } + + public Query addParameter(Date date) { + return addParameterInternal((ps, idx) -> ps.setDate(idx, date)); + } + + public Query addParameter(Timestamp timestamp) { + return addParameterInternal((ps, idx) -> ps.setTimestamp(idx, timestamp)); + } + + /** + * Serializes {@literal value} to a JSON string for persistence. + * + * @param value The value to serialize. + * @return {@literal this} + */ + public Query addJsonParameter(Object value) { + return addParameter(toJson(value)); + } + + /** + * Bind the given {@link java.util.Date} to the PreparedStatement as a {@link java.sql.Date}. + * + * @param date The {@literal java.util.Date} to bind. + * @return {@literal this} + */ + public Query addDateParameter(java.util.Date date) { + return addParameter(new Date(date.getTime())); + } + + /** + * Bind the given {@link java.util.Date} to the PreparedStatement as a {@link + * java.sql.Timestamp}. + * + * @param date The {@literal java.util.Date} to bind. + * @return {@literal this} + */ + public Query addTimestampParameter(java.util.Date date) { + return addParameter(new Timestamp(date.getTime())); + } + + /** + * Bind the given epoch millis to the PreparedStatement as a {@link java.sql.Timestamp}. + * + * @param epochMillis The epoch ms to create a new {@literal Timestamp} from. + * @return {@literal this} + */ + public Query addTimestampParameter(long epochMillis) { + return addParameter(new Timestamp(epochMillis)); + } + + /** + * Add a collection of primitive values at once, in the order of the collection. + * + * @param values The values to bind to the prepared statement. + * @return {@literal this} + * @throws IllegalArgumentException If a non-primitive/unsupported type is encountered in the + * collection. + * @see #addParameters(Object...) + */ + public Query addParameters(Collection values) { + return addParameters(values.toArray()); + } + + /** + * Add many primitive values at once. + * + * @param values The values to bind to the prepared statement. + * @return {@literal this} + * @throws IllegalArgumentException If a non-primitive/unsupported type is encountered. + */ + public Query addParameters(Object... values) { + for (Object v : values) { + if (v instanceof String) { + addParameter((String) v); + } else if (v instanceof Integer) { + addParameter((Integer) v); + } else if (v instanceof Long) { + addParameter((Long) v); + } else if (v instanceof Double) { + addParameter((Double) v); + } else if (v instanceof Boolean) { + addParameter((Boolean) v); + } else if (v instanceof Date) { + addParameter((Date) v); + } else if (v instanceof Timestamp) { + addParameter((Timestamp) v); + } else { + throw new IllegalArgumentException( + "Type " + + v.getClass().getName() + + " is not supported by automatic property assignment"); + } + } + + return this; + } + + /** + * Utility method for evaluating the prepared statement as a query to check the existence of a + * record using a numeric count or boolean return value. + * + *

The {@link #rawQuery} provided must result in a {@link Number} or {@link Boolean} result. + * + * @return {@literal true} If a count query returned more than 0 or an exists query returns + * {@literal true}. + * @throws NonTransientException If an unexpected return type cannot be evaluated to a {@code + * Boolean} result. + */ + public boolean exists() { + Object val = executeScalar(); + if (null == val) { + return false; + } + + if (val instanceof Number) { + return convertLong(val) > 0; + } + + if (val instanceof Boolean) { + return (Boolean) val; + } + + if (val instanceof String) { + return convertBoolean(val); + } + + throw new NonTransientException( + "Expected a Numeric or Boolean scalar return value from the query, received " + + val.getClass().getName()); + } + + /** + * Convenience method for executing delete statements. + * + * @return {@literal true} if the statement affected 1 or more rows. + * @see #executeUpdate() + */ + public boolean executeDelete() { + int count = executeUpdate(); + if (count > 1) { + logger.trace("Removed {} row(s) for query {}", count, rawQuery); + } + + return count > 0; + } + + /** + * Convenience method for executing statements that return a single numeric value, typically + * {@literal SELECT COUNT...} style queries. + * + * @return The result of the query as a {@literal long}. + */ + public long executeCount() { + return executeScalar(Long.class); + } + + /** + * @return The result of {@link PreparedStatement#executeUpdate()} + */ + public int executeUpdate() { + try { + + Long start = null; + if (logger.isTraceEnabled()) { + start = System.currentTimeMillis(); + } + + final int val = this.statement.executeUpdate(); + + if (null != start && logger.isTraceEnabled()) { + long end = System.currentTimeMillis(); + logger.trace("[{}ms] {}: {}", (end - start), val, rawQuery); + } + + return val; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute a query from the PreparedStatement and return the ResultSet. + * + *

NOTE: The returned ResultSet must be closed/managed by the calling methods. + * + * @return {@link PreparedStatement#executeQuery()} + * @throws NonTransientException If any SQL errors occur. + */ + public ResultSet executeQuery() { + Long start = null; + if (logger.isTraceEnabled()) { + start = System.currentTimeMillis(); + } + + try { + return this.statement.executeQuery(); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + if (null != start && logger.isTraceEnabled()) { + long end = System.currentTimeMillis(); + logger.trace("[{}ms] {}", (end - start), rawQuery); + } + } + } + + /** + * @return The single result of the query as an Object. + */ + public Object executeScalar() { + try (ResultSet rs = executeQuery()) { + if (!rs.next()) { + return null; + } + return rs.getObject(1); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the PreparedStatement and return a single 'primitive' value from the ResultSet. + * + * @param returnType The type to return. + * @param The type parameter to return a List of. + * @return A single result from the execution of the statement, as a type of {@literal + * returnType}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public V executeScalar(Class returnType) { + try (ResultSet rs = executeQuery()) { + if (!rs.next()) { + Object value = null; + if (Integer.class == returnType) { + value = 0; + } else if (Long.class == returnType) { + value = 0L; + } else if (Boolean.class == returnType) { + value = false; + } + return returnType.cast(value); + } else { + return getScalarFromResultSet(rs, returnType); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the PreparedStatement and return a List of 'primitive' values from the ResultSet. + * + * @param returnType The type Class return a List of. + * @param The type parameter to return a List of. + * @return A {@code List}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public List executeScalarList(Class returnType) { + try (ResultSet rs = executeQuery()) { + List values = new ArrayList<>(); + while (rs.next()) { + values.add(getScalarFromResultSet(rs, returnType)); + } + return values; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the statement and return only the first record from the result set. + * + * @param returnType The Class to return. + * @param The type parameter. + * @return An instance of {@literal } from the result set. + */ + public V executeAndFetchFirst(Class returnType) { + Object o = executeScalar(); + if (null == o) { + return null; + } + return convert(o, returnType); + } + + /** + * Execute the PreparedStatement and return a List of {@literal returnType} values from the + * ResultSet. + * + * @param returnType The type Class return a List of. + * @param The type parameter to return a List of. + * @return A {@code List}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public List executeAndFetch(Class returnType) { + try (ResultSet rs = executeQuery()) { + List list = new ArrayList<>(); + while (rs.next()) { + list.add(convert(rs.getObject(1), returnType)); + } + return list; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the query and pass the {@link ResultSet} to the given handler. + * + * @param handler The {@link ResultSetHandler} to execute. + * @param The return type of this method. + * @return The results of {@link ResultSetHandler#apply(ResultSet)}. + */ + public V executeAndFetch(ResultSetHandler handler) { + try (ResultSet rs = executeQuery()) { + return handler.apply(rs); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + @Override + public void close() { + try { + if (null != statement && !statement.isClosed()) { + statement.close(); + } + } catch (SQLException ex) { + logger.warn("Error closing prepared statement: {}", ex.getMessage()); + } + } + + protected final Query addParameterInternal(InternalParameterSetter setter) { + int index = getAndIncrementIndex(); + try { + setter.apply(this.statement, index); + return this; + } catch (SQLException ex) { + throw new NonTransientException("Could not apply bind parameter at index " + index, ex); + } + } + + protected V getScalarFromResultSet(ResultSet rs, Class returnType) throws SQLException { + Object value = null; + + if (Integer.class == returnType) { + value = rs.getInt(1); + } else if (Long.class == returnType) { + value = rs.getLong(1); + } else if (String.class == returnType) { + value = rs.getString(1); + } else if (Boolean.class == returnType) { + value = rs.getBoolean(1); + } else if (Double.class == returnType) { + value = rs.getDouble(1); + } else if (Date.class == returnType) { + value = rs.getDate(1); + } else if (Timestamp.class == returnType) { + value = rs.getTimestamp(1); + } else { + value = rs.getObject(1); + } + + if (null == value) { + throw new NullPointerException( + "Cannot get value from ResultSet of type " + returnType.getName()); + } + + return returnType.cast(value); + } + + protected V convert(Object value, Class returnType) { + if (Boolean.class == returnType) { + return returnType.cast(convertBoolean(value)); + } else if (Integer.class == returnType) { + return returnType.cast(convertInt(value)); + } else if (Long.class == returnType) { + return returnType.cast(convertLong(value)); + } else if (Double.class == returnType) { + return returnType.cast(convertDouble(value)); + } else if (String.class == returnType) { + return returnType.cast(convertString(value)); + } else if (value instanceof String) { + return fromJson((String) value, returnType); + } + + final String vName = value.getClass().getName(); + final String rName = returnType.getName(); + throw new NonTransientException("Cannot convert type " + vName + " to " + rName); + } + + protected Integer convertInt(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Integer) { + return (Integer) value; + } + + if (value instanceof Number) { + return ((Number) value).intValue(); + } + + return NumberUtils.toInt(value.toString()); + } + + protected Double convertDouble(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Double) { + return (Double) value; + } + + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + + return NumberUtils.toDouble(value.toString()); + } + + protected Long convertLong(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Long) { + return (Long) value; + } + + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return NumberUtils.toLong(value.toString()); + } + + protected String convertString(Object value) { + if (null == value) { + return null; + } + + if (value instanceof String) { + return (String) value; + } + + return value.toString().trim(); + } + + protected Boolean convertBoolean(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Boolean) { + return (Boolean) value; + } + + if (value instanceof Number) { + return ((Number) value).intValue() != 0; + } + + String text = value.toString().trim(); + return "Y".equalsIgnoreCase(text) + || "YES".equalsIgnoreCase(text) + || "TRUE".equalsIgnoreCase(text) + || "T".equalsIgnoreCase(text) + || "1".equalsIgnoreCase(text); + } + + protected String toJson(Object value) { + if (null == value) { + return null; + } + + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected V fromJson(String value, Class returnType) { + if (null == value) { + return null; + } + + try { + return objectMapper.readValue(value, returnType); + } catch (IOException ex) { + throw new NonTransientException( + "Could not convert JSON '" + value + "' to " + returnType.getName(), ex); + } + } + + protected final int getIndex() { + return index.get(); + } + + protected final int getAndIncrementIndex() { + return index.getAndIncrement(); + } + + @FunctionalInterface + private interface InternalParameterSetter { + + void apply(PreparedStatement ps, int idx) throws SQLException; + } +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java new file mode 100644 index 000000000..135f22425 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java @@ -0,0 +1,25 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.sql.SQLException; + +/** + * Functional interface for {@link Query} executions that return results. + * + * @author mustafa + */ +@FunctionalInterface +public interface QueryFunction { + + R apply(Query query) throws SQLException; +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java new file mode 100644 index 000000000..14c6d6108 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java @@ -0,0 +1,26 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Functional interface for {@link Query#executeAndFetch(ResultSetHandler)}. + * + * @author mustafa + */ +@FunctionalInterface +public interface ResultSetHandler { + + R apply(ResultSet resultSet) throws SQLException; +} diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java new file mode 100644 index 000000000..f82eb4d42 --- /dev/null +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java @@ -0,0 +1,26 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.util; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Functional interface for operations within a transactional context. + * + * @author mustafa + */ +@FunctionalInterface +public interface TransactionalFunction { + + R apply(Connection tx) throws SQLException; +} diff --git a/mysql-persistence/src/main/resources/db/migration/V1__initial_schema.sql b/mysql-persistence/src/main/resources/db/migration/V1__initial_schema.sql new file mode 100644 index 000000000..246b55ecd --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V1__initial_schema.sql @@ -0,0 +1,172 @@ + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR METADATA DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE meta_event_handler ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + event varchar(255) NOT NULL, + active boolean NOT NULL, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + KEY event_handler_name_index (name), + KEY event_handler_event_index (event) +); + +CREATE TABLE meta_task_def ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_task_def_name (name) +); + +CREATE TABLE meta_workflow_def ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + version int(11) NOT NULL, + latest_version int(11) NOT NULL DEFAULT 0, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_name_version (name,version), + KEY workflow_def_name_index (name) +); + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR EXECUTION DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE event_execution ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + event_handler_name varchar(255) NOT NULL, + event_name varchar(255) NOT NULL, + message_id varchar(255) NOT NULL, + execution_id varchar(255) NOT NULL, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_event_execution (event_handler_name,event_name,message_id) +); + +CREATE TABLE poll_data ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + domain varchar(255) NOT NULL, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_poll_data (queue_name,domain), + KEY (queue_name) +); + +CREATE TABLE task_scheduled ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + task_key varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_workflow_id_task_key (workflow_id,task_key) +); + +CREATE TABLE task_in_progress ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + task_def_name varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + workflow_id varchar(255) NOT NULL, + in_progress_status boolean NOT NULL DEFAULT false, + PRIMARY KEY (id), + UNIQUE KEY unique_task_def_task_id1 (task_def_name,task_id) +); + +CREATE TABLE task ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + task_id varchar(255) NOT NULL, + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_task_id (task_id) +); + +CREATE TABLE workflow ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + correlation_id varchar(255), + json_data mediumtext NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_workflow_id (workflow_id) +); + +CREATE TABLE workflow_def_to_workflow ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_def varchar(255) NOT NULL, + date_str integer NOT NULL, + workflow_id varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_workflow_def_date_str (workflow_def,date_str,workflow_id) +); + +CREATE TABLE workflow_pending ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_type varchar(255) NOT NULL, + workflow_id varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_workflow_type_workflow_id (workflow_type,workflow_id), + KEY workflow_type_index (workflow_type) +); + +CREATE TABLE workflow_to_task ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_workflow_to_task_id (workflow_id,task_id), + KEY workflow_id_index (workflow_id) +); + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR QUEUE DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE queue ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_queue_name (queue_name) +); + +CREATE TABLE queue_message ( + id int(11) unsigned NOT NULL AUTO_INCREMENT, + created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deliver_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + message_id varchar(255) NOT NULL, + popped boolean DEFAULT false, + offset_time_seconds long, + payload mediumtext, + PRIMARY KEY (id), + UNIQUE KEY unique_queue_name_message_id (queue_name,message_id), + KEY combo_queue_message (queue_name,popped,deliver_on,created_on) +); diff --git a/mysql-persistence/src/main/resources/db/migration/V2__queue_message_timestamps.sql b/mysql-persistence/src/main/resources/db/migration/V2__queue_message_timestamps.sql new file mode 100644 index 000000000..ecf7956be --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V2__queue_message_timestamps.sql @@ -0,0 +1,2 @@ +ALTER TABLE `queue_message` CHANGE `created_on` `created_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE `queue_message` CHANGE `deliver_on` `deliver_on` TIMESTAMP DEFAULT CURRENT_TIMESTAMP; diff --git a/mysql-persistence/src/main/resources/db/migration/V3__queue_add_priority.sql b/mysql-persistence/src/main/resources/db/migration/V3__queue_add_priority.sql new file mode 100644 index 000000000..2764df8b3 --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V3__queue_add_priority.sql @@ -0,0 +1,17 @@ +SET @dbname = DATABASE(); +SET @tablename = "queue_message"; +SET @columnname = "priority"; +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE + (table_name = @tablename) + AND (table_schema = @dbname) + AND (column_name = @columnname) + ) > 0, + "SELECT 1", + CONCAT("ALTER TABLE ", @tablename, " ADD ", @columnname, " TINYINT DEFAULT 0 AFTER `message_id`") +)); +PREPARE addColumnIfNotExist FROM @preparedStatement; +EXECUTE addColumnIfNotExist; +DEALLOCATE PREPARE addColumnIfNotExist; \ No newline at end of file diff --git a/mysql-persistence/src/main/resources/db/migration/V4__1009_Fix_MySQLExecutionDAO_Index.sql b/mysql-persistence/src/main/resources/db/migration/V4__1009_Fix_MySQLExecutionDAO_Index.sql new file mode 100644 index 000000000..8787961a8 --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V4__1009_Fix_MySQLExecutionDAO_Index.sql @@ -0,0 +1,14 @@ +# Drop the 'unique_event_execution' index if it exists +SET @exist := (SELECT COUNT(INDEX_NAME) + FROM information_schema.STATISTICS + WHERE `TABLE_NAME` = 'event_execution' + AND `INDEX_NAME` = 'unique_event_execution' + AND TABLE_SCHEMA = database()); +SET @sqlstmt := IF(@exist > 0, 'ALTER TABLE `event_execution` DROP INDEX `unique_event_execution`', + 'SELECT ''INFO: Index already exists.'''); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +# Create the 'unique_event_execution' index with execution_id column instead of 'message_id' so events can be executed multiple times. +ALTER TABLE `event_execution` + ADD CONSTRAINT `unique_event_execution` UNIQUE (event_handler_name, event_name, execution_id); diff --git a/mysql-persistence/src/main/resources/db/migration/V5__correlation_id_index.sql b/mysql-persistence/src/main/resources/db/migration/V5__correlation_id_index.sql new file mode 100644 index 000000000..2f13789f3 --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V5__correlation_id_index.sql @@ -0,0 +1,13 @@ +# Drop the 'workflow_corr_id_index' index if it exists +SET @exist := (SELECT COUNT(INDEX_NAME) + FROM information_schema.STATISTICS + WHERE `TABLE_NAME` = 'workflow' + AND `INDEX_NAME` = 'workflow_corr_id_index' + AND TABLE_SCHEMA = database()); +SET @sqlstmt := IF(@exist > 0, 'ALTER TABLE `workflow` DROP INDEX `workflow_corr_id_index`', + 'SELECT ''INFO: Index already exists.'''); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +# Create the 'workflow_corr_id_index' index with correlation_id column because correlation_id queries are slow in large databases. +CREATE INDEX workflow_corr_id_index ON workflow (correlation_id); diff --git a/mysql-persistence/src/main/resources/db/migration/V6__new_qm_index_with_priority.sql b/mysql-persistence/src/main/resources/db/migration/V6__new_qm_index_with_priority.sql new file mode 100644 index 000000000..de591f972 --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V6__new_qm_index_with_priority.sql @@ -0,0 +1,13 @@ +# Drop the 'combo_queue_message' index if it exists +SET @exist := (SELECT COUNT(INDEX_NAME) + FROM information_schema.STATISTICS + WHERE `TABLE_NAME` = 'queue_message' + AND `INDEX_NAME` = 'combo_queue_message' + AND TABLE_SCHEMA = database()); +SET @sqlstmt := IF(@exist > 0, 'ALTER TABLE `queue_message` DROP INDEX `combo_queue_message`', + 'SELECT ''INFO: Index already exists.'''); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +# Re-create the 'combo_queue_message' index to add priority column because queries that order by priority are slow in large databases. +CREATE INDEX combo_queue_message ON queue_message (queue_name,priority,popped,deliver_on,created_on); diff --git a/mysql-persistence/src/main/resources/db/migration/V7__new_queue_message_pk.sql b/mysql-persistence/src/main/resources/db/migration/V7__new_queue_message_pk.sql new file mode 100644 index 000000000..afad02024 --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V7__new_queue_message_pk.sql @@ -0,0 +1,24 @@ +# no longer need separate index if pk is queue_name, message_id +SET @idx_exists := (SELECT COUNT(INDEX_NAME) + FROM information_schema.STATISTICS + WHERE `TABLE_NAME` = 'queue_message' + AND `INDEX_NAME` = 'unique_queue_name_message_id' + AND TABLE_SCHEMA = database()); +SET @idxstmt := IF(@idx_exists > 0, 'ALTER TABLE `queue_message` DROP INDEX `unique_queue_name_message_id`', + 'SELECT ''INFO: Index unique_queue_name_message_id does not exist.'''); +PREPARE stmt1 FROM @idxstmt; +EXECUTE stmt1; + +# remove id column +set @col_exists := (SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE `TABLE_NAME` = 'queue_message' + AND `COLUMN_NAME` = 'id' + AND TABLE_SCHEMA = database()); +SET @colstmt := IF(@col_exists > 0, 'ALTER TABLE `queue_message` DROP COLUMN `id`', + 'SELECT ''INFO: Column id does not exist.''') ; +PREPARE stmt2 from @colstmt; +EXECUTE stmt2; + +# set primary key to queue_name, message_id +ALTER TABLE queue_message ADD PRIMARY KEY (queue_name, message_id); diff --git a/mysql-persistence/src/main/resources/db/migration/V8__update_pk.sql b/mysql-persistence/src/main/resources/db/migration/V8__update_pk.sql new file mode 100644 index 000000000..f1ed4f7ad --- /dev/null +++ b/mysql-persistence/src/main/resources/db/migration/V8__update_pk.sql @@ -0,0 +1,103 @@ +DELIMITER $$ +DROP PROCEDURE IF EXISTS `DropIndexIfExists`$$ +CREATE PROCEDURE `DropIndexIfExists`(IN tableName VARCHAR(128), IN indexName VARCHAR(128)) +BEGIN + + DECLARE index_exists INT DEFAULT 0; + + SELECT COUNT(1) INTO index_exists + FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_NAME = tableName + AND INDEX_NAME = indexName + AND TABLE_SCHEMA = database(); + + IF index_exists > 0 THEN + + SELECT CONCAT('INFO: Dropping Index ', indexName, ' on table ', tableName); + SET @stmt = CONCAT('ALTER TABLE ', tableName, ' DROP INDEX ', indexName); + PREPARE st FROM @stmt; + EXECUTE st; + DEALLOCATE PREPARE st; + + ELSE + SELECT CONCAT('INFO: Index ', indexName, ' does not exists on table ', tableName); + END IF; + +END$$ + +DROP PROCEDURE IF EXISTS `FixPkIfNeeded`$$ +CREATE PROCEDURE `FixPkIfNeeded`(IN tableName VARCHAR(128), IN columns VARCHAR(128)) +BEGIN + + DECLARE col_exists INT DEFAULT 0; + + SELECT COUNT(1) INTO col_exists + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = tableName + AND COLUMN_NAME = 'id' + AND TABLE_SCHEMA = database(); + + IF col_exists > 0 THEN + + SELECT CONCAT('INFO: Updating PK on table ', tableName); + + SET @stmt = CONCAT('ALTER TABLE ', tableName, ' MODIFY id INT'); + PREPARE st FROM @stmt; + EXECUTE st; + DEALLOCATE PREPARE st; + + SET @stmt = CONCAT('ALTER TABLE ', tableName, ' DROP PRIMARY KEY, ADD PRIMARY KEY (', columns, ')'); + PREPARE st FROM @stmt; + EXECUTE st; + DEALLOCATE PREPARE st; + + SET @stmt = CONCAT('ALTER TABLE ', tableName, ' DROP COLUMN id'); + PREPARE st FROM @stmt; + EXECUTE st; + DEALLOCATE PREPARE st; + + ELSE + SELECT CONCAT('INFO: Column id does not exists on table ', tableName); + END IF; + +END$$ +DELIMITER ; + +CALL DropIndexIfExists('queue_message', 'unique_queue_name_message_id'); +CALL FixPkIfNeeded('queue_message','queue_name, message_id'); + +CALL DropIndexIfExists('queue', 'unique_queue_name'); +CALL FixPkIfNeeded('queue','queue_name'); + +CALL DropIndexIfExists('workflow_to_task', 'unique_workflow_to_task_id'); +CALL FixPkIfNeeded('workflow_to_task', 'workflow_id, task_id'); + +CALL DropIndexIfExists('workflow_pending', 'unique_workflow_type_workflow_id'); +CALL FixPkIfNeeded('workflow_pending', 'workflow_type, workflow_id'); + +CALL DropIndexIfExists('workflow_def_to_workflow', 'unique_workflow_def_date_str'); +CALL FixPkIfNeeded('workflow_def_to_workflow', 'workflow_def, date_str, workflow_id'); + +CALL DropIndexIfExists('workflow', 'unique_workflow_id'); +CALL FixPkIfNeeded('workflow', 'workflow_id'); + +CALL DropIndexIfExists('task', 'unique_task_id'); +CALL FixPkIfNeeded('task', 'task_id'); + +CALL DropIndexIfExists('task_in_progress', 'unique_task_def_task_id1'); +CALL FixPkIfNeeded('task_in_progress', 'task_def_name, task_id'); + +CALL DropIndexIfExists('task_scheduled', 'unique_workflow_id_task_key'); +CALL FixPkIfNeeded('task_scheduled', 'workflow_id, task_key'); + +CALL DropIndexIfExists('poll_data', 'unique_poll_data'); +CALL FixPkIfNeeded('poll_data','queue_name, domain'); + +CALL DropIndexIfExists('event_execution', 'unique_event_execution'); +CALL FixPkIfNeeded('event_execution', 'event_handler_name, event_name, execution_id'); + +CALL DropIndexIfExists('meta_workflow_def', 'unique_name_version'); +CALL FixPkIfNeeded('meta_workflow_def', 'name, version'); + +CALL DropIndexIfExists('meta_task_def', 'unique_task_def_name'); +CALL FixPkIfNeeded('meta_task_def','name'); \ No newline at end of file diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java new file mode 100644 index 000000000..366205724 --- /dev/null +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java @@ -0,0 +1,80 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.util.List; + +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.dao.ExecutionDAO; +import com.netflix.conductor.dao.ExecutionDAOTest; +import com.netflix.conductor.model.WorkflowModel; +import com.netflix.conductor.mysql.config.MySQLConfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + MySQLConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class MySQLExecutionDAOTest extends ExecutionDAOTest { + + @Autowired private MySQLExecutionDAO executionDAO; + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.clean(); + flyway.migrate(); + } + + @Test + public void testPendingByCorrelationId() { + + WorkflowDef def = new WorkflowDef(); + def.setName("pending_count_correlation_jtest"); + + WorkflowModel workflow = createTestWorkflow(); + workflow.setWorkflowDefinition(def); + + generateWorkflows(workflow, 10); + + List bycorrelationId = + getExecutionDAO() + .getWorkflowsByCorrelationId( + "pending_count_correlation_jtest", "corr001", true); + assertNotNull(bycorrelationId); + assertEquals(10, bycorrelationId.size()); + } + + @Override + public ExecutionDAO getExecutionDAO() { + return executionDAO; + } +} diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java new file mode 100644 index 000000000..a357985bc --- /dev/null +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java @@ -0,0 +1,321 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.mysql.config.MySQLConfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + MySQLConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class MySQLMetadataDAOTest { + + @Autowired private MySQLMetadataDAO metadataDAO; + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.clean(); + flyway.migrate(); + } + + @Test + public void testDuplicateWorkflowDef() { + + WorkflowDef def = new WorkflowDef(); + def.setName("testDuplicate"); + def.setVersion(1); + + metadataDAO.createWorkflowDef(def); + + NonTransientException applicationException = + assertThrows(NonTransientException.class, () -> metadataDAO.createWorkflowDef(def)); + assertEquals( + "Workflow with testDuplicate.1 already exists!", applicationException.getMessage()); + } + + @Test + public void testRemoveNotExistingWorkflowDef() { + NonTransientException applicationException = + assertThrows( + NonTransientException.class, + () -> metadataDAO.removeWorkflowDef("test", 1)); + assertEquals( + "No such workflow definition: test version: 1", applicationException.getMessage()); + } + + @Test + public void testWorkflowDefOperations() { + WorkflowDef def = new WorkflowDef(); + def.setName("test"); + def.setVersion(1); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setOwnerApp("ownerApp"); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + + metadataDAO.createWorkflowDef(def); + + List all = metadataDAO.getAllWorkflowDefs(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(1, all.get(0).getVersion()); + + WorkflowDef found = metadataDAO.getWorkflowDef("test", 1).get(); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + + def.setVersion(3); + metadataDAO.createWorkflowDef(def); + + all = metadataDAO.getAllWorkflowDefs(); + assertNotNull(all); + assertEquals(2, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(1, all.get(0).getVersion()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(def.getVersion(), found.getVersion()); + assertEquals(3, found.getVersion()); + + all = metadataDAO.getAllLatest(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(3, all.get(0).getVersion()); + + all = metadataDAO.getAllVersions(def.getName()); + assertNotNull(all); + assertEquals(2, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals("test", all.get(1).getName()); + assertEquals(1, all.get(0).getVersion()); + assertEquals(3, all.get(1).getVersion()); + + def.setDescription("updated"); + metadataDAO.updateWorkflowDef(def); + found = metadataDAO.getWorkflowDef(def.getName(), def.getVersion()).get(); + assertEquals(def.getDescription(), found.getDescription()); + + List allnames = metadataDAO.findAll(); + assertNotNull(allnames); + assertEquals(1, allnames.size()); + assertEquals(def.getName(), allnames.get(0)); + + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(3, found.getVersion()); + + metadataDAO.removeWorkflowDef("test", 3); + Optional deleted = metadataDAO.getWorkflowDef("test", 3); + assertFalse(deleted.isPresent()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(2, found.getVersion()); + + metadataDAO.removeWorkflowDef("test", 1); + deleted = metadataDAO.getWorkflowDef("test", 1); + assertFalse(deleted.isPresent()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(2, found.getVersion()); + } + + @Test + public void testTaskDefOperations() { + TaskDef def = new TaskDef("taskA"); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setInputKeys(Arrays.asList("a", "b", "c")); + def.setOutputKeys(Arrays.asList("01", "o2")); + def.setOwnerApp("ownerApp"); + def.setRetryCount(3); + def.setRetryDelaySeconds(100); + def.setRetryLogic(TaskDef.RetryLogic.FIXED); + def.setTimeoutPolicy(TaskDef.TimeoutPolicy.ALERT_ONLY); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + + metadataDAO.createTaskDef(def); + + TaskDef found = metadataDAO.getTaskDef(def.getName()); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + + def.setDescription("updated description"); + metadataDAO.updateTaskDef(def); + found = metadataDAO.getTaskDef(def.getName()); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + assertEquals("updated description", found.getDescription()); + + for (int i = 0; i < 9; i++) { + TaskDef tdf = new TaskDef("taskA" + i); + metadataDAO.createTaskDef(tdf); + } + + List all = metadataDAO.getAllTaskDefs(); + assertNotNull(all); + assertEquals(10, all.size()); + Set allnames = all.stream().map(TaskDef::getName).collect(Collectors.toSet()); + assertEquals(10, allnames.size()); + List sorted = allnames.stream().sorted().collect(Collectors.toList()); + assertEquals(def.getName(), sorted.get(0)); + + for (int i = 0; i < 9; i++) { + assertEquals(def.getName() + i, sorted.get(i + 1)); + } + + for (int i = 0; i < 9; i++) { + metadataDAO.removeTaskDef(def.getName() + i); + } + all = metadataDAO.getAllTaskDefs(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals(def.getName(), all.get(0).getName()); + } + + @Test + public void testRemoveNotExistingTaskDef() { + NonTransientException applicationException = + assertThrows( + NonTransientException.class, + () -> metadataDAO.removeTaskDef("test" + UUID.randomUUID().toString())); + assertEquals("No such task definition", applicationException.getMessage()); + } + + @Test + public void testEventHandlers() { + String event1 = "SQS::arn:account090:sqstest1"; + String event2 = "SQS::arn:account090:sqstest2"; + + EventHandler eventHandler = new EventHandler(); + eventHandler.setName(UUID.randomUUID().toString()); + eventHandler.setActive(false); + EventHandler.Action action = new EventHandler.Action(); + action.setAction(EventHandler.Action.Type.start_workflow); + action.setStart_workflow(new EventHandler.StartWorkflow()); + action.getStart_workflow().setName("workflow_x"); + eventHandler.getActions().add(action); + eventHandler.setEvent(event1); + + metadataDAO.addEventHandler(eventHandler); + List all = metadataDAO.getAllEventHandlers(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals(eventHandler.getName(), all.get(0).getName()); + assertEquals(eventHandler.getEvent(), all.get(0).getEvent()); + + List byEvents = metadataDAO.getEventHandlersForEvent(event1, true); + assertNotNull(byEvents); + assertEquals(0, byEvents.size()); // event is marked as in-active + + eventHandler.setActive(true); + eventHandler.setEvent(event2); + metadataDAO.updateEventHandler(eventHandler); + + all = metadataDAO.getAllEventHandlers(); + assertNotNull(all); + assertEquals(1, all.size()); + + byEvents = metadataDAO.getEventHandlersForEvent(event1, true); + assertNotNull(byEvents); + assertEquals(0, byEvents.size()); + + byEvents = metadataDAO.getEventHandlersForEvent(event2, true); + assertNotNull(byEvents); + assertEquals(1, byEvents.size()); + } + + @Test + public void testGetAllWorkflowDefsLatestVersions() { + WorkflowDef def = new WorkflowDef(); + def.setName("test1"); + def.setVersion(1); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setOwnerApp("ownerApp"); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + metadataDAO.createWorkflowDef(def); + + def.setName("test2"); + metadataDAO.createWorkflowDef(def); + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + + def.setName("test3"); + def.setVersion(1); + metadataDAO.createWorkflowDef(def); + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + def.setVersion(3); + metadataDAO.createWorkflowDef(def); + + // Placed the values in a map because they might not be stored in order of defName. + // To test, needed to confirm that the versions are correct for the definitions. + Map allMap = + metadataDAO.getAllWorkflowDefsLatestVersions().stream() + .collect(Collectors.toMap(WorkflowDef::getName, Function.identity())); + + assertNotNull(allMap); + assertEquals(3, allMap.size()); + assertEquals(1, allMap.get("test1").getVersion()); + assertEquals(2, allMap.get("test2").getVersion()); + assertEquals(3, allMap.get("test3").getVersion()); + } +} diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java new file mode 100644 index 000000000..9bf095320 --- /dev/null +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java @@ -0,0 +1,384 @@ +/* + *

+ * 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 com.netflix.conductor.mysql.dao; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.mysql.config.MySQLConfiguration; +import com.netflix.conductor.mysql.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + MySQLConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class MySQLQueueDAOTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(MySQLQueueDAOTest.class); + + @Autowired private MySQLQueueDAO queueDAO; + + @Autowired private ObjectMapper objectMapper; + + @Qualifier("dataSource") + @Autowired + private DataSource dataSource; + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.clean(); + flyway.migrate(); + } + + @Test + public void complexQueueTest() { + String queueName = "TestQueue"; + long offsetTimeInSecond = 0; + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.push(queueName, messageId, offsetTimeInSecond); + } + int size = queueDAO.getSize(queueName); + assertEquals(10, size); + Map details = queueDAO.queuesDetail(); + assertEquals(1, details.size()); + assertEquals(10L, details.get(queueName).longValue()); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + + List popped = queueDAO.pop(queueName, 10, 100); + assertNotNull(popped); + assertEquals(10, popped.size()); + + Map>> verbose = queueDAO.queuesDetailVerbose(); + assertEquals(1, verbose.size()); + long shardSize = verbose.get(queueName).get("a").get("size"); + long unackedSize = verbose.get(queueName).get("a").get("uacked"); + assertEquals(0, shardSize); + assertEquals(10, unackedSize); + + popped.forEach(messageId -> queueDAO.ack(queueName, messageId)); + + verbose = queueDAO.queuesDetailVerbose(); + assertEquals(1, verbose.size()); + shardSize = verbose.get(queueName).get("a").get("size"); + unackedSize = verbose.get(queueName).get("a").get("uacked"); + assertEquals(0, shardSize); + assertEquals(0, unackedSize); + + popped = queueDAO.pop(queueName, 10, 100); + assertNotNull(popped); + assertEquals(0, popped.size()); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + size = queueDAO.getSize(queueName); + assertEquals(10, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertTrue(queueDAO.containsMessage(queueName, messageId)); + queueDAO.remove(queueName, messageId); + } + + size = queueDAO.getSize(queueName); + assertEquals(0, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + queueDAO.flush(queueName); + size = queueDAO.getSize(queueName); + assertEquals(0, size); + } + + /** Test fix for https://github.com/Netflix/conductor/issues/1892 */ + @Test + public void containsMessageTest() { + String queueName = "TestQueue"; + long offsetTimeInSecond = 0; + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.push(queueName, messageId, offsetTimeInSecond); + } + int size = queueDAO.getSize(queueName); + assertEquals(10, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertTrue(queueDAO.containsMessage(queueName, messageId)); + queueDAO.remove(queueName, messageId); + } + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertFalse(queueDAO.containsMessage(queueName, messageId)); + } + } + + /** + * Test fix for https://github.com/Netflix/conductor/issues/399 + * + * @since 1.8.2-rc5 + */ + @Test + public void pollMessagesTest() { + final List messages = new ArrayList<>(); + final String queueName = "issue399_testQueue"; + final int totalSize = 10; + + for (int i = 0; i < totalSize; i++) { + String payload = "{\"id\": " + i + ", \"msg\":\"test " + i + "\"}"; + Message m = new Message("testmsg-" + i, payload, ""); + if (i % 2 == 0) { + // Set priority on message with pair id + m.setPriority(99 - i); + } + messages.add(m); + } + + // Populate the queue with our test message batch + queueDAO.push(queueName, ImmutableList.copyOf(messages)); + + // Assert that all messages were persisted and no extras are in there + assertEquals("Queue size mismatch", totalSize, queueDAO.getSize(queueName)); + + final int firstPollSize = 3; + List firstPoll = queueDAO.pollMessages(queueName, firstPollSize, 10_000); + assertNotNull("First poll was null", firstPoll); + assertFalse("First poll was empty", firstPoll.isEmpty()); + assertEquals("First poll size mismatch", firstPollSize, firstPoll.size()); + + final int secondPollSize = 4; + List secondPoll = queueDAO.pollMessages(queueName, secondPollSize, 10_000); + assertNotNull("Second poll was null", secondPoll); + assertFalse("Second poll was empty", secondPoll.isEmpty()); + assertEquals("Second poll size mismatch", secondPollSize, secondPoll.size()); + + // Assert that the total queue size hasn't changed + assertEquals( + "Total queue size should have remained the same", + totalSize, + queueDAO.getSize(queueName)); + + // Assert that our un-popped messages match our expected size + final long expectedSize = totalSize - firstPollSize - secondPollSize; + try (Connection c = dataSource.getConnection()) { + String UNPOPPED = + "SELECT COUNT(*) FROM queue_message WHERE queue_name = ? AND popped = false"; + try (Query q = new Query(objectMapper, c, UNPOPPED)) { + long count = q.addParameter(queueName).executeCount(); + assertEquals("Remaining queue size mismatch", expectedSize, count); + } + } catch (Exception ex) { + fail(ex.getMessage()); + } + } + + /** + * Test fix for https://github.com/Netflix/conductor/issues/448 + * + * @since 1.8.2-rc5 + */ + @Test + public void pollDeferredMessagesTest() throws InterruptedException { + final List messages = new ArrayList<>(); + final String queueName = "issue448_testQueue"; + final int totalSize = 10; + + for (int i = 0; i < totalSize; i++) { + int offset = 0; + if (i < 5) { + offset = 0; + } else if (i == 6 || i == 7) { + // Purposefully skipping id:5 to test out of order deliveries + // Set id:6 and id:7 for a 2s delay to be picked up in the second polling batch + offset = 5; + } else { + // Set all other queue messages to have enough of a delay that they won't + // accidentally + // be picked up. + offset = 10_000 + i; + } + + String payload = "{\"id\": " + i + ",\"offset_time_seconds\":" + offset + "}"; + Message m = new Message("testmsg-" + i, payload, ""); + messages.add(m); + queueDAO.push(queueName, "testmsg-" + i, offset); + } + + // Assert that all messages were persisted and no extras are in there + assertEquals("Queue size mismatch", totalSize, queueDAO.getSize(queueName)); + + final int firstPollSize = 4; + List firstPoll = queueDAO.pollMessages(queueName, firstPollSize, 100); + assertNotNull("First poll was null", firstPoll); + assertFalse("First poll was empty", firstPoll.isEmpty()); + assertEquals("First poll size mismatch", firstPollSize, firstPoll.size()); + + List firstPollMessageIds = + messages.stream() + .map(Message::getId) + .collect(Collectors.toList()) + .subList(0, firstPollSize + 1); + + for (int i = 0; i < firstPollSize; i++) { + String actual = firstPoll.get(i).getId(); + assertTrue("Unexpected Id: " + actual, firstPollMessageIds.contains(actual)); + } + + final int secondPollSize = 3; + + // Sleep a bit to get the next batch of messages + LOGGER.debug("Sleeping for second poll..."); + Thread.sleep(5_000); + + // Poll for many more messages than expected + List secondPoll = queueDAO.pollMessages(queueName, secondPollSize + 10, 100); + assertNotNull("Second poll was null", secondPoll); + assertFalse("Second poll was empty", secondPoll.isEmpty()); + assertEquals("Second poll size mismatch", secondPollSize, secondPoll.size()); + + List expectedIds = Arrays.asList("testmsg-4", "testmsg-6", "testmsg-7"); + for (int i = 0; i < secondPollSize; i++) { + String actual = secondPoll.get(i).getId(); + assertTrue("Unexpected Id: " + actual, expectedIds.contains(actual)); + } + + // Assert that the total queue size hasn't changed + assertEquals( + "Total queue size should have remained the same", + totalSize, + queueDAO.getSize(queueName)); + + // Assert that our un-popped messages match our expected size + final long expectedSize = totalSize - firstPollSize - secondPollSize; + try (Connection c = dataSource.getConnection()) { + String UNPOPPED = + "SELECT COUNT(*) FROM queue_message WHERE queue_name = ? AND popped = false"; + try (Query q = new Query(objectMapper, c, UNPOPPED)) { + long count = q.addParameter(queueName).executeCount(); + assertEquals("Remaining queue size mismatch", expectedSize, count); + } + } catch (Exception ex) { + fail(ex.getMessage()); + } + } + + @Test + public void processUnacksTest() { + final String queueName = "process_unacks_test"; + // Count of messages in the queue(s) + final int count = 10; + // Number of messages to process acks for + final int unackedCount = 4; + // A secondary queue to make sure we don't accidentally process other queues + final String otherQueueName = "process_unacks_test_other_queue"; + + // Create testing queue with some messages (but not all) that will be popped/acked. + for (int i = 0; i < count; i++) { + int offset = 0; + if (i >= unackedCount) { + offset = 1_000_000; + } + + queueDAO.push(queueName, "unack-" + i, offset); + } + + // Create a second queue to make sure that unacks don't occur for it + for (int i = 0; i < count; i++) { + queueDAO.push(otherQueueName, "other-" + i, 0); + } + + // Poll for first batch of messages (should be equal to unackedCount) + List polled = queueDAO.pollMessages(queueName, 100, 10_000); + assertNotNull(polled); + assertFalse(polled.isEmpty()); + assertEquals(unackedCount, polled.size()); + + // Poll messages from the other queue so we know they don't get unacked later + queueDAO.pollMessages(otherQueueName, 100, 10_000); + + // Ack one of the polled messages + assertTrue(queueDAO.ack(queueName, "unack-1")); + + // Should have one less un-acked popped message in the queue + Long uacked = queueDAO.queuesDetailVerbose().get(queueName).get("a").get("uacked"); + assertNotNull(uacked); + assertEquals(uacked.longValue(), unackedCount - 1); + + // Process unacks + queueDAO.processUnacks(queueName); + + // Check uacks for both queues after processing + Map>> details = queueDAO.queuesDetailVerbose(); + uacked = details.get(queueName).get("a").get("uacked"); + assertNotNull(uacked); + assertEquals( + "The messages that were polled should be unacked still", + uacked.longValue(), + unackedCount - 1); + + Long otherUacked = details.get(otherQueueName).get("a").get("uacked"); + assertNotNull(otherUacked); + assertEquals( + "Other queue should have all unacked messages", otherUacked.longValue(), count); + + Long size = queueDAO.queuesDetail().get(queueName); + assertNotNull(size); + assertEquals(size.longValue(), count - unackedCount); + } +} diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java new file mode 100644 index 000000000..a5210a88a --- /dev/null +++ b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java @@ -0,0 +1,47 @@ +/* + *

+ * 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 com.netflix.conductor.test.integration.grpc.mysql; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.client.grpc.EventClient; +import com.netflix.conductor.client.grpc.MetadataClient; +import com.netflix.conductor.client.grpc.TaskClient; +import com.netflix.conductor.client.grpc.WorkflowClient; +import com.netflix.conductor.test.integration.grpc.AbstractGrpcEndToEndTest; + +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "conductor.db.type=mysql", + "conductor.grpc-server.port=8094", + "spring.datasource.url=jdbc:tc:mysql:8.0.27:///conductor", // "tc" prefix starts the + // MySql + // container + "spring.datasource.username=root", + "spring.datasource.password=root", + "spring.datasource.hikari.maximum-pool-size=8", + "spring.datasource.hikari.minimum-idle=300000" + }) +public class MySQLGrpcEndToEndTest extends AbstractGrpcEndToEndTest { + + @Before + public void init() { + taskClient = new TaskClient("localhost", 8094); + workflowClient = new WorkflowClient("localhost", 8094); + metadataClient = new MetadataClient("localhost", 8094); + eventClient = new EventClient("localhost", 8094); + } +} diff --git a/mysql-persistence/src/test/resources/application.properties b/mysql-persistence/src/test/resources/application.properties new file mode 100644 index 000000000..d9e7b87cb --- /dev/null +++ b/mysql-persistence/src/test/resources/application.properties @@ -0,0 +1,6 @@ +conductor.db.type=mysql +spring.datasource.url=jdbc:tc:mysql:8.0.27:///conductor +spring.datasource.username=root +spring.datasource.password=root +spring.datasource.hikari.maximum-pool-size=8 +spring.datasource.hikari.auto-commit=false diff --git a/postgres-persistence/build.gradle b/postgres-persistence/build.gradle new file mode 100644 index 000000000..d0fef0332 --- /dev/null +++ b/postgres-persistence/build.gradle @@ -0,0 +1,43 @@ +dependencies { + + implementation project(':conductor-common-persistence') + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.retry:spring-retry' + + implementation "com.google.guava:guava:${revGuava}" + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + + implementation "org.apache.commons:commons-lang3" + implementation "org.postgresql:postgresql:42.3.8" + implementation "org.springframework.boot:spring-boot-starter-jdbc" + implementation "org.flywaydb:flyway-core" + + testImplementation "org.apache.groovy:groovy-all:${revGroovy}" + +// testImplementation "org.elasticsearch:elasticsearch:6.8.23" +// testImplementation "org.elasticsearch.client:transport:6.8.23" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" + testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" + testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" + + testImplementation project(':conductor-server') + testImplementation project(':conductor-client') + testImplementation project(':conductor-grpc-client') + testImplementation project(':conductor-es6-persistence') + + testImplementation "org.testcontainers:postgresql:${revTestContainer}" + + testImplementation project(':conductor-test-util').sourceSets.test.output + testImplementation project(':conductor-common-persistence').sourceSets.test.output + +} + +test { + //the SQL unit tests must run within the same JVM to share the same embedded DB + maxParallelForks = 1 +} diff --git a/postgres-persistence/dependencies.lock b/postgres-persistence/dependencies.lock new file mode 100644 index 000000000..39d58d5e9 --- /dev/null +++ b/postgres-persistence/dependencies.lock @@ -0,0 +1,991 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "3.1.4" + } + }, + "compileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.flywaydb:flyway-core": { + "locked": "9.21.0" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "locked": "2.0.3" + } + }, + "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.flywaydb:flyway-core": { + "locked": "9.21.0" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + } + }, + "testCompileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.15.2" + }, + "com.google.guava:guava": { + "locked": "30.0-jre" + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "locked": "6.8.23" + }, + "org.elasticsearch:elasticsearch": { + "locked": "6.8.23" + }, + "org.flywaydb:flyway-core": { + "locked": "9.21.0" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + } + }, + "testRuntimeClasspath": { + "com.amazonaws:aws-java-sdk-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-s3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage" + ], + "locked": "1.11.86" + }, + "com.amazonaws:aws-java-sdk-sqs": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue" + ], + "locked": "1.11.86" + }, + "com.datastax.cassandra:cassandra-driver-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-cassandra-persistence" + ], + "locked": "3.10.2" + }, + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "3.1.8" + }, + "com.google.guava:guava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "3.22.3" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true + }, + "com.netflix.conductor:conductor-awss3-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-awssqs-event-queue": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-cassandra-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-client": { + "project": true + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-es6-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-grpc-client": { + "project": true + }, + "com.netflix.conductor:conductor-grpc-server": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-http-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-json-jq-task": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-concurrency-limit": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-lock": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-rest": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-server": { + "project": true + }, + "com.netflix.dyno-queues:dyno-queues-redis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "2.0.20" + }, + "com.netflix.eureka:eureka-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.10.10" + }, + "com.netflix.runtime:health-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest" + ], + "locked": "1.1.4" + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "com.sun.jersey:jersey-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "1.19.4" + }, + "com.thoughtworks.xstream:xstream": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "1.4.20" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "2.7" + }, + "io.grpc:grpc-netty": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-protobuf": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-services": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc-server" + ], + "locked": "1.57.2" + }, + "io.grpc:grpc-stub": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "1.57.2" + }, + "io.orkes.queues:orkes-conductor-queues": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "1.0.7" + }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.annotation:jakarta.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.1.1" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "javax.annotation:javax.annotation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-grpc" + ], + "locked": "1.3.2" + }, + "javax.ws.rs:javax.ws.rs-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "2.1.1" + }, + "javax.ws.rs:jsr311-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.thisptr:jackson-jq": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-json-jq-task" + ], + "locked": "0.0.13" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.12.0" + }, + "org.apache.groovy:groovy-all": { + "locked": "4.0.9" + }, + "org.apache.httpcomponents.client5:httpclient5": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-http-task" + ], + "locked": "5.2.1" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-awss3-storage", + "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-grpc", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-lock", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.20.0" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch.client:transport": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es6-persistence" + ], + "locked": "6.8.23" + }, + "org.elasticsearch:elasticsearch": { + "locked": "6.8.23" + }, + "org.flywaydb:flyway-core": { + "locked": "9.21.0" + }, + "org.glassfish.jaxb:jaxb-runtime": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "4.0.3" + }, + "org.glassfish.jersey.core:jersey-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client" + ], + "locked": "3.1.3" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.9.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.rarefiedredis.redis:redis-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-persistence" + ], + "locked": "0.0.17" + }, + "org.redisson:redisson": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-lock" + ], + "locked": "3.13.3" + }, + "org.slf4j:slf4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-client", + "com.netflix.conductor:conductor-grpc-client" + ], + "locked": "2.0.9" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "2.1.0" + }, + "org.springframework.boot:spring-boot-starter": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-actuator": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-validation": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.boot:spring-boot-starter-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.1.4" + }, + "org.springframework.retry:spring-retry": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "locked": "2.0.3" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.15.3" + }, + "org.testcontainers:postgresql": { + "locked": "1.15.3" + }, + "redis.clients:jedis": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-redis-concurrency-limit", + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-server" + ], + "locked": "3.3.0" + } + } +} \ No newline at end of file diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java new file mode 100644 index 000000000..e07c63243 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java @@ -0,0 +1,144 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.config; + +import java.sql.SQLException; +import java.util.Optional; + +import javax.sql.DataSource; + +import jakarta.annotation.*; +import org.flywaydb.core.Flyway; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.*; +import org.springframework.core.env.*; +import org.springframework.retry.RetryContext; +import org.springframework.retry.backoff.NoBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.postgres.dao.PostgresExecutionDAO; +import com.netflix.conductor.postgres.dao.PostgresIndexDAO; +import com.netflix.conductor.postgres.dao.PostgresMetadataDAO; +import com.netflix.conductor.postgres.dao.PostgresQueueDAO; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(PostgresProperties.class) +@ConditionalOnProperty(name = "conductor.db.type", havingValue = "postgres") +// Import the DataSourceAutoConfiguration when postgres database is selected. +// By default, the datasource configuration is excluded in the main module. +@Import(DataSourceAutoConfiguration.class) +public class PostgresConfiguration { + + DataSource dataSource; + + private final PostgresProperties properties; + + public PostgresConfiguration(DataSource dataSource, PostgresProperties properties) { + this.dataSource = dataSource; + this.properties = properties; + } + + @Bean(initMethod = "migrate") + @PostConstruct + public Flyway flywayForPrimaryDb() { + return Flyway.configure() + .locations("classpath:db/migration_postgres") + .schemas(properties.getSchema()) + .dataSource(dataSource) + .outOfOrder(true) + .baselineOnMigrate(true) + .load(); + } + + @Bean + @DependsOn({"flywayForPrimaryDb"}) + public PostgresMetadataDAO postgresMetadataDAO( + @Qualifier("postgresRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper, + PostgresProperties properties) { + return new PostgresMetadataDAO(retryTemplate, objectMapper, dataSource, properties); + } + + @Bean + @DependsOn({"flywayForPrimaryDb"}) + public PostgresExecutionDAO postgresExecutionDAO( + @Qualifier("postgresRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper) { + return new PostgresExecutionDAO(retryTemplate, objectMapper, dataSource); + } + + @Bean + @DependsOn({"flywayForPrimaryDb"}) + public PostgresQueueDAO postgresQueueDAO( + @Qualifier("postgresRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper) { + return new PostgresQueueDAO(retryTemplate, objectMapper, dataSource); + } + + @Bean + @DependsOn({"flywayForPrimaryDb"}) + @ConditionalOnProperty(name = "conductor.indexing.type", havingValue = "postgres") + public PostgresIndexDAO postgresIndexDAO( + @Qualifier("postgresRetryTemplate") RetryTemplate retryTemplate, + ObjectMapper objectMapper) { + return new PostgresIndexDAO(retryTemplate, objectMapper, dataSource); + } + + @Bean + public RetryTemplate postgresRetryTemplate(PostgresProperties properties) { + SimpleRetryPolicy retryPolicy = new CustomRetryPolicy(); + retryPolicy.setMaxAttempts(3); + + RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(new NoBackOffPolicy()); + return retryTemplate; + } + + public static class CustomRetryPolicy extends SimpleRetryPolicy { + + private static final String ER_LOCK_DEADLOCK = "40P01"; + private static final String ER_SERIALIZATION_FAILURE = "40001"; + + @Override + public boolean canRetry(final RetryContext context) { + final Optional lastThrowable = + Optional.ofNullable(context.getLastThrowable()); + return lastThrowable + .map(throwable -> super.canRetry(context) && isDeadLockError(throwable)) + .orElseGet(() -> super.canRetry(context)); + } + + private boolean isDeadLockError(Throwable throwable) { + SQLException sqlException = findCauseSQLException(throwable); + if (sqlException == null) { + return false; + } + return ER_LOCK_DEADLOCK.equals(sqlException.getSQLState()) + || ER_SERIALIZATION_FAILURE.equals(sqlException.getSQLState()); + } + + private SQLException findCauseSQLException(Throwable throwable) { + Throwable causeException = throwable; + while (null != causeException && !(causeException instanceof SQLException)) { + causeException = causeException.getCause(); + } + return (SQLException) causeException; + } + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java new file mode 100644 index 000000000..038faf987 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java @@ -0,0 +1,54 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.config; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; + +@ConfigurationProperties("conductor.postgres") +public class PostgresProperties { + + /** The time in seconds after which the in-memory task definitions cache will be refreshed */ + @DurationUnit(ChronoUnit.SECONDS) + private Duration taskDefCacheRefreshInterval = Duration.ofSeconds(60); + + private Integer deadlockRetryMax = 3; + + public String schema = "public"; + + public Duration getTaskDefCacheRefreshInterval() { + return taskDefCacheRefreshInterval; + } + + public void setTaskDefCacheRefreshInterval(Duration taskDefCacheRefreshInterval) { + this.taskDefCacheRefreshInterval = taskDefCacheRefreshInterval; + } + + public Integer getDeadlockRetryMax() { + return deadlockRetryMax; + } + + public void setDeadlockRetryMax(Integer deadlockRetryMax) { + this.deadlockRetryMax = deadlockRetryMax; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java new file mode 100644 index 000000000..694510638 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java @@ -0,0 +1,255 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.postgres.util.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; + +public abstract class PostgresBaseDAO { + + private static final List EXCLUDED_STACKTRACE_CLASS = + ImmutableList.of(PostgresBaseDAO.class.getName(), Thread.class.getName()); + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final ObjectMapper objectMapper; + protected final DataSource dataSource; + + private final RetryTemplate retryTemplate; + + protected PostgresBaseDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + this.retryTemplate = retryTemplate; + this.objectMapper = objectMapper; + this.dataSource = dataSource; + } + + protected final LazyToString getCallingMethod() { + return new LazyToString( + () -> + Arrays.stream(Thread.currentThread().getStackTrace()) + .filter( + ste -> + !EXCLUDED_STACKTRACE_CLASS.contains( + ste.getClassName())) + .findFirst() + .map(StackTraceElement::getMethodName) + .orElseThrow(() -> new NullPointerException("Cannot find Caller"))); + } + + protected String toJson(Object value) { + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected T readValue(String json, Class tClass) { + try { + return objectMapper.readValue(json, tClass); + } catch (IOException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected T readValue(String json, TypeReference typeReference) { + try { + return objectMapper.readValue(json, typeReference); + } catch (IOException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Initialize a new transactional {@link Connection} from {@link #dataSource} and pass it to + * {@literal function}. + * + *

Successful executions of {@literal function} will result in a commit and return of {@link + * TransactionalFunction#apply(Connection)}. + * + *

If any {@link Throwable} thrown from {@code TransactionalFunction#apply(Connection)} will + * result in a rollback of the transaction and will be wrapped in an {@link + * NonTransientException} if it is not already one. + * + *

Generally this is used to wrap multiple {@link #execute(Connection, String, + * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that + * produce some expected return value. + * + * @param function The function to apply with a new transactional {@link Connection} + * @param The return type. + * @return The result of {@code TransactionalFunction#apply(Connection)} + * @throws NonTransientException If any errors occur. + */ + private R getWithTransaction(final TransactionalFunction function) { + final Instant start = Instant.now(); + LazyToString callingMethod = getCallingMethod(); + logger.trace("{} : starting transaction", callingMethod); + + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(false); + try { + R result = function.apply(tx); + tx.commit(); + return result; + } catch (Throwable th) { + tx.rollback(); + if (th instanceof NonTransientException) { + throw th; + } + throw new NonTransientException(th.getMessage(), th); + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + logger.trace( + "{} : took {}ms", + callingMethod, + Duration.between(start, Instant.now()).toMillis()); + } + } + + R getWithRetriedTransactions(final TransactionalFunction function) { + try { + return retryTemplate.execute(context -> getWithTransaction(function)); + } catch (Exception e) { + throw new NonTransientException(e.getMessage(), e); + } + } + + protected R getWithTransactionWithOutErrorPropagation(TransactionalFunction function) { + Instant start = Instant.now(); + LazyToString callingMethod = getCallingMethod(); + logger.trace("{} : starting transaction", callingMethod); + + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(false); + try { + R result = function.apply(tx); + tx.commit(); + return result; + } catch (Throwable th) { + tx.rollback(); + logger.info(th.getMessage()); + return null; + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + logger.trace( + "{} : took {}ms", + callingMethod, + Duration.between(start, Instant.now()).toMillis()); + } + } + + /** + * Wraps {@link #getWithRetriedTransactions(TransactionalFunction)} with no return value. + * + *

Generally this is used to wrap multiple {@link #execute(Connection, String, + * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that + * produce no expected return value. + * + * @param consumer The {@link Consumer} callback to pass a transactional {@link Connection} to. + * @throws NonTransientException If any errors occur. + * @see #getWithRetriedTransactions(TransactionalFunction) + */ + protected void withTransaction(Consumer consumer) { + getWithRetriedTransactions( + connection -> { + consumer.accept(connection); + return null; + }); + } + + /** + * Initiate a new transaction and execute a {@link Query} within that context, then return the + * results of {@literal function}. + * + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + * @param The expected return type of {@literal function}. + * @return The results of applying {@literal function}. + */ + protected R queryWithTransaction(String query, QueryFunction function) { + return getWithRetriedTransactions(tx -> query(tx, query, function)); + } + + /** + * Execute a {@link Query} within the context of a given transaction and return the results of + * {@literal function}. + * + * @param tx The transactional {@link Connection} to use. + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + * @param The expected return type of {@literal function}. + * @return The results of applying {@literal function}. + */ + protected R query(Connection tx, String query, QueryFunction function) { + try (Query q = new Query(objectMapper, tx, query)) { + return function.apply(q); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute a statement with no expected return value within a given transaction. + * + * @param tx The transactional {@link Connection} to use. + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + */ + protected void execute(Connection tx, String query, ExecuteFunction function) { + try (Query q = new Query(objectMapper, tx, query)) { + function.apply(q); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Instantiates a new transactional connection and invokes {@link #execute(Connection, String, + * ExecuteFunction)} + * + * @param query The query string to prepare. + * @param function The functional callback to pass a {@link Query} to. + */ + protected void executeWithTransaction(String query, ExecuteFunction function) { + withTransaction(tx -> execute(tx, query, function)); + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java new file mode 100644 index 000000000..af9e5edaf --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java @@ -0,0 +1,1099 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import jakarta.annotation.*; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.common.metadata.events.EventExecution; +import com.netflix.conductor.common.metadata.tasks.PollData; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.dao.ConcurrentExecutionLimitDAO; +import com.netflix.conductor.dao.ExecutionDAO; +import com.netflix.conductor.dao.PollDataDAO; +import com.netflix.conductor.dao.RateLimitingDAO; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.model.TaskModel; +import com.netflix.conductor.model.WorkflowModel; +import com.netflix.conductor.postgres.util.ExecutorsUtil; +import com.netflix.conductor.postgres.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public class PostgresExecutionDAO extends PostgresBaseDAO + implements ExecutionDAO, RateLimitingDAO, PollDataDAO, ConcurrentExecutionLimitDAO { + + private final ScheduledExecutorService scheduledExecutorService; + + public PostgresExecutionDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + super(retryTemplate, objectMapper, dataSource); + this.scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor( + ExecutorsUtil.newNamedThreadFactory("postgres-execution-")); + } + + private static String dateStr(Long timeInMs) { + Date date = new Date(timeInMs); + return dateStr(date); + } + + private static String dateStr(Date date) { + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + return format.format(date); + } + + @PreDestroy + public void destroy() { + try { + this.scheduledExecutorService.shutdown(); + if (scheduledExecutorService.awaitTermination(30, TimeUnit.SECONDS)) { + logger.debug("tasks completed, shutting down"); + } else { + logger.warn("Forcing shutdown after waiting for 30 seconds"); + scheduledExecutorService.shutdownNow(); + } + } catch (InterruptedException ie) { + logger.warn( + "Shutdown interrupted, invoking shutdownNow on scheduledExecutorService for removeWorkflowWithExpiry", + ie); + scheduledExecutorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + public List getPendingTasksByWorkflow(String taskDefName, String workflowId) { + // @formatter:off + String GET_IN_PROGRESS_TASKS_FOR_WORKFLOW = + "SELECT json_data FROM task_in_progress tip " + + "INNER JOIN task t ON t.task_id = tip.task_id " + + "WHERE task_def_name = ? AND workflow_id = ? FOR SHARE"; + // @formatter:on + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_FOR_WORKFLOW, + q -> + q.addParameter(taskDefName) + .addParameter(workflowId) + .executeAndFetch(TaskModel.class)); + } + + @Override + public List getTasks(String taskDefName, String startKey, int count) { + List tasks = new ArrayList<>(count); + + List pendingTasks = getPendingTasksForTaskType(taskDefName); + boolean startKeyFound = startKey == null; + int found = 0; + for (TaskModel pendingTask : pendingTasks) { + if (!startKeyFound) { + if (pendingTask.getTaskId().equals(startKey)) { + startKeyFound = true; + // noinspection ConstantConditions + if (startKey != null) { + continue; + } + } + } + if (startKeyFound && found < count) { + tasks.add(pendingTask); + found++; + } + } + + return tasks; + } + + private static String taskKey(TaskModel task) { + return task.getReferenceTaskName() + "_" + task.getRetryCount(); + } + + @Override + public List createTasks(List tasks) { + List created = Lists.newArrayListWithCapacity(tasks.size()); + + for (TaskModel task : tasks) { + withTransaction( + connection -> { + validate(task); + + task.setScheduledTime(System.currentTimeMillis()); + + final String taskKey = taskKey(task); + + boolean scheduledTaskAdded = addScheduledTask(connection, task, taskKey); + + if (!scheduledTaskAdded) { + logger.trace( + "Task already scheduled, skipping the run " + + task.getTaskId() + + ", ref=" + + task.getReferenceTaskName() + + ", key=" + + taskKey); + return; + } + + insertOrUpdateTaskData(connection, task); + addWorkflowToTaskMapping(connection, task); + addTaskInProgress(connection, task); + updateTask(connection, task); + + created.add(task); + }); + } + + return created; + } + + @Override + public void updateTask(TaskModel task) { + withTransaction(connection -> updateTask(connection, task)); + } + + /** + * This is a dummy implementation and this feature is not for Postgres backed Conductor + * + * @param task: which needs to be evaluated whether it is rateLimited or not + */ + @Override + public boolean exceedsRateLimitPerFrequency(TaskModel task, TaskDef taskDef) { + return false; + } + + @Override + public boolean exceedsLimit(TaskModel task) { + + Optional taskDefinition = task.getTaskDefinition(); + if (taskDefinition.isEmpty()) { + return false; + } + + TaskDef taskDef = taskDefinition.get(); + + int limit = taskDef.concurrencyLimit(); + if (limit <= 0) { + return false; + } + + long current = getInProgressTaskCount(task.getTaskDefName()); + + if (current >= limit) { + Monitors.recordTaskConcurrentExecutionLimited(task.getTaskDefName(), limit); + return true; + } + + logger.info( + "Task execution count for {}: limit={}, current={}", + task.getTaskDefName(), + limit, + getInProgressTaskCount(task.getTaskDefName())); + + String taskId = task.getTaskId(); + + List tasksInProgressInOrderOfArrival = + findAllTasksInProgressInOrderOfArrival(task, limit); + + boolean rateLimited = !tasksInProgressInOrderOfArrival.contains(taskId); + + if (rateLimited) { + logger.info( + "Task execution count limited. {}, limit {}, current {}", + task.getTaskDefName(), + limit, + getInProgressTaskCount(task.getTaskDefName())); + Monitors.recordTaskConcurrentExecutionLimited(task.getTaskDefName(), limit); + } + + return rateLimited; + } + + @Override + public boolean removeTask(String taskId) { + TaskModel task = getTask(taskId); + + if (task == null) { + logger.warn("No such task found by id {}", taskId); + return false; + } + + final String taskKey = taskKey(task); + + withTransaction( + connection -> { + removeScheduledTask(connection, task, taskKey); + removeWorkflowToTaskMapping(connection, task); + removeTaskInProgress(connection, task); + removeTaskData(connection, task); + }); + return true; + } + + @Override + public TaskModel getTask(String taskId) { + String GET_TASK = "SELECT json_data FROM task WHERE task_id = ?"; + return queryWithTransaction( + GET_TASK, q -> q.addParameter(taskId).executeAndFetchFirst(TaskModel.class)); + } + + @Override + public List getTasks(List taskIds) { + if (taskIds.isEmpty()) { + return Lists.newArrayList(); + } + return getWithRetriedTransactions(c -> getTasks(c, taskIds)); + } + + @Override + public List getPendingTasksForTaskType(String taskName) { + Preconditions.checkNotNull(taskName, "task name cannot be null"); + // @formatter:off + String GET_IN_PROGRESS_TASKS_FOR_TYPE = + "SELECT json_data FROM task_in_progress tip " + + "INNER JOIN task t ON t.task_id = tip.task_id " + + "WHERE task_def_name = ? FOR UPDATE SKIP LOCKED"; + // @formatter:on + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_FOR_TYPE, + q -> q.addParameter(taskName).executeAndFetch(TaskModel.class)); + } + + @Override + public List getTasksForWorkflow(String workflowId) { + String GET_TASKS_FOR_WORKFLOW = + "SELECT task_id FROM workflow_to_task WHERE workflow_id = ? FOR SHARE"; + return getWithRetriedTransactions( + tx -> + query( + tx, + GET_TASKS_FOR_WORKFLOW, + q -> { + List taskIds = + q.addParameter(workflowId) + .executeScalarList(String.class); + return getTasks(tx, taskIds); + })); + } + + @Override + public String createWorkflow(WorkflowModel workflow) { + return insertOrUpdateWorkflow(workflow, false); + } + + @Override + public String updateWorkflow(WorkflowModel workflow) { + return insertOrUpdateWorkflow(workflow, true); + } + + @Override + public boolean removeWorkflow(String workflowId) { + boolean removed = false; + WorkflowModel workflow = getWorkflow(workflowId, true); + if (workflow != null) { + withTransaction( + connection -> { + removeWorkflowDefToWorkflowMapping(connection, workflow); + removeWorkflow(connection, workflowId); + removePendingWorkflow(connection, workflow.getWorkflowName(), workflowId); + }); + removed = true; + + for (TaskModel task : workflow.getTasks()) { + if (!removeTask(task.getTaskId())) { + removed = false; + } + } + } + return removed; + } + + /** Scheduled executor based implementation. */ + @Override + public boolean removeWorkflowWithExpiry(String workflowId, int ttlSeconds) { + scheduledExecutorService.schedule( + () -> { + try { + removeWorkflow(workflowId); + } catch (Throwable e) { + logger.warn("Unable to remove workflow: {} with expiry", workflowId, e); + } + }, + ttlSeconds, + TimeUnit.SECONDS); + + return true; + } + + @Override + public void removeFromPendingWorkflow(String workflowType, String workflowId) { + withTransaction(connection -> removePendingWorkflow(connection, workflowType, workflowId)); + } + + @Override + public WorkflowModel getWorkflow(String workflowId) { + return getWorkflow(workflowId, true); + } + + @Override + public WorkflowModel getWorkflow(String workflowId, boolean includeTasks) { + WorkflowModel workflow = getWithRetriedTransactions(tx -> readWorkflow(tx, workflowId)); + + if (workflow != null) { + if (includeTasks) { + List tasks = getTasksForWorkflow(workflowId); + tasks.sort(Comparator.comparingInt(TaskModel::getSeq)); + workflow.setTasks(tasks); + } + } + return workflow; + } + + /** + * @param workflowName name of the workflow + * @param version the workflow version + * @return list of workflow ids that are in RUNNING state returns workflows of all versions + * for the given workflow name + */ + @Override + public List getRunningWorkflowIds(String workflowName, int version) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + String GET_PENDING_WORKFLOW_IDS = + "SELECT workflow_id FROM workflow_pending WHERE workflow_type = ? FOR SHARE SKIP LOCKED"; + + return queryWithTransaction( + GET_PENDING_WORKFLOW_IDS, + q -> q.addParameter(workflowName).executeScalarList(String.class)); + } + + /** + * @param workflowName Name of the workflow + * @param version the workflow version + * @return list of workflows that are in RUNNING state + */ + @Override + public List getPendingWorkflowsByType(String workflowName, int version) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + return getRunningWorkflowIds(workflowName, version).stream() + .map(this::getWorkflow) + .filter(workflow -> workflow.getWorkflowVersion() == version) + .collect(Collectors.toList()); + } + + @Override + public long getPendingWorkflowCount(String workflowName) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + String GET_PENDING_WORKFLOW_COUNT = + "SELECT COUNT(*) FROM workflow_pending WHERE workflow_type = ?"; + + return queryWithTransaction( + GET_PENDING_WORKFLOW_COUNT, q -> q.addParameter(workflowName).executeCount()); + } + + @Override + public long getInProgressTaskCount(String taskDefName) { + String GET_IN_PROGRESS_TASK_COUNT = + "SELECT COUNT(*) FROM task_in_progress WHERE task_def_name = ? AND in_progress_status = true"; + + return queryWithTransaction( + GET_IN_PROGRESS_TASK_COUNT, q -> q.addParameter(taskDefName).executeCount()); + } + + @Override + public List getWorkflowsByType( + String workflowName, Long startTime, Long endTime) { + Preconditions.checkNotNull(workflowName, "workflowName cannot be null"); + Preconditions.checkNotNull(startTime, "startTime cannot be null"); + Preconditions.checkNotNull(endTime, "endTime cannot be null"); + + List workflows = new LinkedList<>(); + + withTransaction( + tx -> { + // @formatter:off + String GET_ALL_WORKFLOWS_FOR_WORKFLOW_DEF = + "SELECT workflow_id FROM workflow_def_to_workflow " + + "WHERE workflow_def = ? AND date_str BETWEEN ? AND ? FOR SHARE SKIP LOCKED"; + // @formatter:on + + List workflowIds = + query( + tx, + GET_ALL_WORKFLOWS_FOR_WORKFLOW_DEF, + q -> + q.addParameter(workflowName) + .addParameter(dateStr(startTime)) + .addParameter(dateStr(endTime)) + .executeScalarList(String.class)); + workflowIds.forEach( + workflowId -> { + try { + WorkflowModel wf = getWorkflow(workflowId); + if (wf.getCreateTime() >= startTime + && wf.getCreateTime() <= endTime) { + workflows.add(wf); + } + } catch (Exception e) { + logger.error( + "Unable to load workflow id {} with name {}", + workflowId, + workflowName, + e); + } + }); + }); + + return workflows; + } + + @Override + public List getWorkflowsByCorrelationId( + String workflowName, String correlationId, boolean includeTasks) { + Preconditions.checkNotNull(correlationId, "correlationId cannot be null"); + String GET_WORKFLOWS_BY_CORRELATION_ID = + "SELECT w.json_data FROM workflow w left join workflow_def_to_workflow wd on w.workflow_id = wd.workflow_id WHERE w.correlation_id = ? and wd.workflow_def = ? FOR SHARE SKIP LOCKED"; + + return queryWithTransaction( + GET_WORKFLOWS_BY_CORRELATION_ID, + q -> + q.addParameter(correlationId) + .addParameter(workflowName) + .executeAndFetch(WorkflowModel.class)); + } + + @Override + public boolean canSearchAcrossWorkflows() { + return true; + } + + @Override + public boolean addEventExecution(EventExecution eventExecution) { + try { + return getWithRetriedTransactions(tx -> insertEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to add event execution " + eventExecution.getId(), e); + } + } + + @Override + public void removeEventExecution(EventExecution eventExecution) { + try { + withTransaction(tx -> removeEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to remove event execution " + eventExecution.getId(), e); + } + } + + @Override + public void updateEventExecution(EventExecution eventExecution) { + try { + withTransaction(tx -> updateEventExecution(tx, eventExecution)); + } catch (Exception e) { + throw new NonTransientException( + "Unable to update event execution " + eventExecution.getId(), e); + } + } + + public List getEventExecutions( + String eventHandlerName, String eventName, String messageId, int max) { + try { + List executions = Lists.newLinkedList(); + withTransaction( + tx -> { + for (int i = 0; i < max; i++) { + String executionId = + messageId + "_" + + i; // see SimpleEventProcessor.handle to understand + // how the + // execution id is set + EventExecution ee = + readEventExecution( + tx, + eventHandlerName, + eventName, + messageId, + executionId); + if (ee == null) { + break; + } + executions.add(ee); + } + }); + return executions; + } catch (Exception e) { + String message = + String.format( + "Unable to get event executions for eventHandlerName=%s, eventName=%s, messageId=%s", + eventHandlerName, eventName, messageId); + throw new NonTransientException(message, e); + } + } + + @Override + public void updateLastPollData(String taskDefName, String domain, String workerId) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + PollData pollData = new PollData(taskDefName, domain, workerId, System.currentTimeMillis()); + String effectiveDomain = (domain == null) ? "DEFAULT" : domain; + withTransaction(tx -> insertOrUpdatePollData(tx, pollData, effectiveDomain)); + } + + @Override + public PollData getPollData(String taskDefName, String domain) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + String effectiveDomain = (domain == null) ? "DEFAULT" : domain; + return getWithRetriedTransactions(tx -> readPollData(tx, taskDefName, effectiveDomain)); + } + + @Override + public List getPollData(String taskDefName) { + Preconditions.checkNotNull(taskDefName, "taskDefName name cannot be null"); + return readAllPollData(taskDefName); + } + + @Override + public List getAllPollData() { + try (Connection tx = dataSource.getConnection()) { + boolean previousAutoCommitMode = tx.getAutoCommit(); + tx.setAutoCommit(true); + try { + String GET_ALL_POLL_DATA = "SELECT json_data FROM poll_data ORDER BY queue_name"; + return query(tx, GET_ALL_POLL_DATA, q -> q.executeAndFetch(PollData.class)); + } catch (Throwable th) { + throw new NonTransientException(th.getMessage(), th); + } finally { + tx.setAutoCommit(previousAutoCommitMode); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + private List getTasks(Connection connection, List taskIds) { + if (taskIds.isEmpty()) { + return Lists.newArrayList(); + } + + // Generate a formatted query string with a variable number of bind params based + // on taskIds.size() + final String GET_TASKS_FOR_IDS = + String.format( + "SELECT json_data FROM task WHERE task_id IN (%s) AND json_data IS NOT NULL", + Query.generateInBindings(taskIds.size())); + + return query( + connection, + GET_TASKS_FOR_IDS, + q -> q.addParameters(taskIds).executeAndFetch(TaskModel.class)); + } + + private String insertOrUpdateWorkflow(WorkflowModel workflow, boolean update) { + Preconditions.checkNotNull(workflow, "workflow object cannot be null"); + + boolean terminal = workflow.getStatus().isTerminal(); + + List tasks = workflow.getTasks(); + workflow.setTasks(Lists.newLinkedList()); + + withTransaction( + tx -> { + if (!update) { + addWorkflow(tx, workflow); + addWorkflowDefToWorkflowMapping(tx, workflow); + } else { + updateWorkflow(tx, workflow); + } + + if (terminal) { + removePendingWorkflow( + tx, workflow.getWorkflowName(), workflow.getWorkflowId()); + } else { + addPendingWorkflow( + tx, workflow.getWorkflowName(), workflow.getWorkflowId()); + } + }); + + workflow.setTasks(tasks); + return workflow.getWorkflowId(); + } + + private void updateTask(Connection connection, TaskModel task) { + Optional taskDefinition = task.getTaskDefinition(); + + if (taskDefinition.isPresent() && taskDefinition.get().concurrencyLimit() > 0) { + boolean inProgress = + task.getStatus() != null + && task.getStatus().equals(TaskModel.Status.IN_PROGRESS); + updateInProgressStatus(connection, task, inProgress); + } + + insertOrUpdateTaskData(connection, task); + + if (task.getStatus() != null && task.getStatus().isTerminal()) { + removeTaskInProgress(connection, task); + } + + addWorkflowToTaskMapping(connection, task); + } + + private WorkflowModel readWorkflow(Connection connection, String workflowId) { + String GET_WORKFLOW = "SELECT json_data FROM workflow WHERE workflow_id = ?"; + + return query( + connection, + GET_WORKFLOW, + q -> q.addParameter(workflowId).executeAndFetchFirst(WorkflowModel.class)); + } + + private void addWorkflow(Connection connection, WorkflowModel workflow) { + String INSERT_WORKFLOW = + "INSERT INTO workflow (workflow_id, correlation_id, json_data) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowId()) + .addParameter(workflow.getCorrelationId()) + .addJsonParameter(workflow) + .executeUpdate()); + } + + private void updateWorkflow(Connection connection, WorkflowModel workflow) { + String UPDATE_WORKFLOW = + "UPDATE workflow SET json_data = ?, modified_on = CURRENT_TIMESTAMP WHERE workflow_id = ?"; + + execute( + connection, + UPDATE_WORKFLOW, + q -> + q.addJsonParameter(workflow) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + private void removeWorkflow(Connection connection, String workflowId) { + String REMOVE_WORKFLOW = "DELETE FROM workflow WHERE workflow_id = ?"; + execute(connection, REMOVE_WORKFLOW, q -> q.addParameter(workflowId).executeDelete()); + } + + private void addPendingWorkflow(Connection connection, String workflowType, String workflowId) { + + String EXISTS_PENDING_WORKFLOW = + "SELECT EXISTS(SELECT 1 FROM workflow_pending WHERE workflow_type = ? AND workflow_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).exists()); + + if (!exists) { + String INSERT_PENDING_WORKFLOW = + "INSERT INTO workflow_pending (workflow_type, workflow_id) VALUES (?, ?) ON CONFLICT (workflow_type,workflow_id) DO NOTHING"; + + execute( + connection, + INSERT_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).executeUpdate()); + } + } + + private void removePendingWorkflow( + Connection connection, String workflowType, String workflowId) { + String REMOVE_PENDING_WORKFLOW = + "DELETE FROM workflow_pending WHERE workflow_type = ? AND workflow_id = ?"; + + execute( + connection, + REMOVE_PENDING_WORKFLOW, + q -> q.addParameter(workflowType).addParameter(workflowId).executeDelete()); + } + + private void insertOrUpdateTaskData(Connection connection, TaskModel task) { + /* + * Most times the row will be updated so let's try the update first. This used to be an 'INSERT/ON CONFLICT do update' sql statement. The problem with that + * is that if we try the INSERT first, the sequence will be increased even if the ON CONFLICT happens. + */ + String UPDATE_TASK = + "UPDATE task SET json_data=?, modified_on=CURRENT_TIMESTAMP WHERE task_id=?"; + int rowsUpdated = + query( + connection, + UPDATE_TASK, + q -> + q.addJsonParameter(task) + .addParameter(task.getTaskId()) + .executeUpdate()); + + if (rowsUpdated == 0) { + String INSERT_TASK = + "INSERT INTO task (task_id, json_data, modified_on) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT (task_id) DO UPDATE SET json_data=excluded.json_data, modified_on=excluded.modified_on"; + execute( + connection, + INSERT_TASK, + q -> q.addParameter(task.getTaskId()).addJsonParameter(task).executeUpdate()); + } + } + + private void removeTaskData(Connection connection, TaskModel task) { + String REMOVE_TASK = "DELETE FROM task WHERE task_id = ?"; + execute(connection, REMOVE_TASK, q -> q.addParameter(task.getTaskId()).executeDelete()); + } + + private void addWorkflowToTaskMapping(Connection connection, TaskModel task) { + + String EXISTS_WORKFLOW_TO_TASK = + "SELECT EXISTS(SELECT 1 FROM workflow_to_task WHERE workflow_id = ? AND task_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .exists()); + + if (!exists) { + String INSERT_WORKFLOW_TO_TASK = + "INSERT INTO workflow_to_task (workflow_id, task_id) VALUES (?, ?) ON CONFLICT (workflow_id,task_id) DO NOTHING"; + + execute( + connection, + INSERT_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + } + + private void removeWorkflowToTaskMapping(Connection connection, TaskModel task) { + String REMOVE_WORKFLOW_TO_TASK = + "DELETE FROM workflow_to_task WHERE workflow_id = ? AND task_id = ?"; + + execute( + connection, + REMOVE_WORKFLOW_TO_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(task.getTaskId()) + .executeDelete()); + } + + private void addWorkflowDefToWorkflowMapping(Connection connection, WorkflowModel workflow) { + String INSERT_WORKFLOW_DEF_TO_WORKFLOW = + "INSERT INTO workflow_def_to_workflow (workflow_def, date_str, workflow_id) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_WORKFLOW_DEF_TO_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowName()) + .addParameter(dateStr(workflow.getCreateTime())) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + private void removeWorkflowDefToWorkflowMapping(Connection connection, WorkflowModel workflow) { + String REMOVE_WORKFLOW_DEF_TO_WORKFLOW = + "DELETE FROM workflow_def_to_workflow WHERE workflow_def = ? AND date_str = ? AND workflow_id = ?"; + + execute( + connection, + REMOVE_WORKFLOW_DEF_TO_WORKFLOW, + q -> + q.addParameter(workflow.getWorkflowName()) + .addParameter(dateStr(workflow.getCreateTime())) + .addParameter(workflow.getWorkflowId()) + .executeUpdate()); + } + + @VisibleForTesting + boolean addScheduledTask(Connection connection, TaskModel task, String taskKey) { + + final String EXISTS_SCHEDULED_TASK = + "SELECT EXISTS(SELECT 1 FROM task_scheduled where workflow_id = ? AND task_key = ?)"; + + boolean exists = + query( + connection, + EXISTS_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .exists()); + + if (!exists) { + final String INSERT_IGNORE_SCHEDULED_TASK = + "INSERT INTO task_scheduled (workflow_id, task_key, task_id) VALUES (?, ?, ?) ON CONFLICT (workflow_id,task_key) DO NOTHING"; + + int count = + query( + connection, + INSERT_IGNORE_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .addParameter(task.getTaskId()) + .executeUpdate()); + return count > 0; + } else { + return false; + } + } + + private void removeScheduledTask(Connection connection, TaskModel task, String taskKey) { + String REMOVE_SCHEDULED_TASK = + "DELETE FROM task_scheduled WHERE workflow_id = ? AND task_key = ?"; + execute( + connection, + REMOVE_SCHEDULED_TASK, + q -> + q.addParameter(task.getWorkflowInstanceId()) + .addParameter(taskKey) + .executeDelete()); + } + + private void addTaskInProgress(Connection connection, TaskModel task) { + String EXISTS_IN_PROGRESS_TASK = + "SELECT EXISTS(SELECT 1 FROM task_in_progress WHERE task_def_name = ? AND task_id = ?)"; + + boolean exists = + query( + connection, + EXISTS_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .exists()); + + if (!exists) { + String INSERT_IN_PROGRESS_TASK = + "INSERT INTO task_in_progress (task_def_name, task_id, workflow_id) VALUES (?, ?, ?)"; + + execute( + connection, + INSERT_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .addParameter(task.getWorkflowInstanceId()) + .executeUpdate()); + } + } + + private void removeTaskInProgress(Connection connection, TaskModel task) { + String REMOVE_IN_PROGRESS_TASK = + "DELETE FROM task_in_progress WHERE task_def_name = ? AND task_id = ?"; + + execute( + connection, + REMOVE_IN_PROGRESS_TASK, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + + private void updateInProgressStatus(Connection connection, TaskModel task, boolean inProgress) { + String UPDATE_IN_PROGRESS_TASK_STATUS = + "UPDATE task_in_progress SET in_progress_status = ?, modified_on = CURRENT_TIMESTAMP " + + "WHERE task_def_name = ? AND task_id = ?"; + + execute( + connection, + UPDATE_IN_PROGRESS_TASK_STATUS, + q -> + q.addParameter(inProgress) + .addParameter(task.getTaskDefName()) + .addParameter(task.getTaskId()) + .executeUpdate()); + } + + private boolean insertEventExecution(Connection connection, EventExecution eventExecution) { + + String INSERT_EVENT_EXECUTION = + "INSERT INTO event_execution (event_handler_name, event_name, message_id, execution_id, json_data) " + + "VALUES (?, ?, ?, ?, ?)"; + int count = + query( + connection, + INSERT_EVENT_EXECUTION, + q -> + q.addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .addJsonParameter(eventExecution) + .executeUpdate()); + return count > 0; + } + + private void updateEventExecution(Connection connection, EventExecution eventExecution) { + // @formatter:off + String UPDATE_EVENT_EXECUTION = + "UPDATE event_execution SET " + + "json_data = ?, " + + "modified_on = CURRENT_TIMESTAMP " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + // @formatter:on + + execute( + connection, + UPDATE_EVENT_EXECUTION, + q -> + q.addJsonParameter(eventExecution) + .addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .executeUpdate()); + } + + private void removeEventExecution(Connection connection, EventExecution eventExecution) { + String REMOVE_EVENT_EXECUTION = + "DELETE FROM event_execution " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + + execute( + connection, + REMOVE_EVENT_EXECUTION, + q -> + q.addParameter(eventExecution.getName()) + .addParameter(eventExecution.getEvent()) + .addParameter(eventExecution.getMessageId()) + .addParameter(eventExecution.getId()) + .executeUpdate()); + } + + private EventExecution readEventExecution( + Connection connection, + String eventHandlerName, + String eventName, + String messageId, + String executionId) { + // @formatter:off + String GET_EVENT_EXECUTION = + "SELECT json_data FROM event_execution " + + "WHERE event_handler_name = ? " + + "AND event_name = ? " + + "AND message_id = ? " + + "AND execution_id = ?"; + // @formatter:on + return query( + connection, + GET_EVENT_EXECUTION, + q -> + q.addParameter(eventHandlerName) + .addParameter(eventName) + .addParameter(messageId) + .addParameter(executionId) + .executeAndFetchFirst(EventExecution.class)); + } + + private void insertOrUpdatePollData(Connection connection, PollData pollData, String domain) { + /* + * Most times the row will be updated so let's try the update first. This used to be an 'INSERT/ON CONFLICT do update' sql statement. The problem with that + * is that if we try the INSERT first, the sequence will be increased even if the ON CONFLICT happens. Since polling happens *a lot*, the sequence can increase + * dramatically even though it won't be used. + */ + String UPDATE_POLL_DATA = + "UPDATE poll_data SET json_data=?, modified_on=CURRENT_TIMESTAMP WHERE queue_name=? AND domain=?"; + int rowsUpdated = + query( + connection, + UPDATE_POLL_DATA, + q -> + q.addJsonParameter(pollData) + .addParameter(pollData.getQueueName()) + .addParameter(domain) + .executeUpdate()); + + if (rowsUpdated == 0) { + String INSERT_POLL_DATA = + "INSERT INTO poll_data (queue_name, domain, json_data, modified_on) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (queue_name,domain) DO UPDATE SET json_data=excluded.json_data, modified_on=excluded.modified_on"; + execute( + connection, + INSERT_POLL_DATA, + q -> + q.addParameter(pollData.getQueueName()) + .addParameter(domain) + .addJsonParameter(pollData) + .executeUpdate()); + } + } + + private PollData readPollData(Connection connection, String queueName, String domain) { + String GET_POLL_DATA = + "SELECT json_data FROM poll_data WHERE queue_name = ? AND domain = ?"; + return query( + connection, + GET_POLL_DATA, + q -> + q.addParameter(queueName) + .addParameter(domain) + .executeAndFetchFirst(PollData.class)); + } + + private List readAllPollData(String queueName) { + String GET_ALL_POLL_DATA = "SELECT json_data FROM poll_data WHERE queue_name = ?"; + return queryWithTransaction( + GET_ALL_POLL_DATA, q -> q.addParameter(queueName).executeAndFetch(PollData.class)); + } + + private List findAllTasksInProgressInOrderOfArrival(TaskModel task, int limit) { + String GET_IN_PROGRESS_TASKS_WITH_LIMIT = + "SELECT task_id FROM task_in_progress WHERE task_def_name = ? ORDER BY created_on LIMIT ?"; + + return queryWithTransaction( + GET_IN_PROGRESS_TASKS_WITH_LIMIT, + q -> + q.addParameter(task.getTaskDefName()) + .addParameter(limit) + .executeScalarList(String.class)); + } + + private void validate(TaskModel task) { + Preconditions.checkNotNull(task, "task object cannot be null"); + Preconditions.checkNotNull(task.getTaskId(), "Task id cannot be null"); + Preconditions.checkNotNull( + task.getWorkflowInstanceId(), "Workflow instance id cannot be null"); + Preconditions.checkNotNull( + task.getReferenceTaskName(), "Task reference name cannot be null"); + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java new file mode 100644 index 000000000..8895af230 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java @@ -0,0 +1,302 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import javax.sql.DataSource; + +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.common.metadata.events.EventExecution; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.dao.IndexDAO; +import com.netflix.conductor.postgres.util.PostgresIndexQueryBuilder; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PostgresIndexDAO extends PostgresBaseDAO implements IndexDAO { + + public PostgresIndexDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + super(retryTemplate, objectMapper, dataSource); + } + + @Override + public void indexWorkflow(WorkflowSummary workflow) { + String INSERT_WORKFLOW_INDEX_SQL = + "INSERT INTO workflow_index (workflow_id, correlation_id, workflow_type, start_time, status, json_data)" + + "VALUES (?, ?, ?, ?, ?, ?::JSONB) ON CONFLICT (workflow_id) \n" + + "DO UPDATE SET correlation_id = EXCLUDED.correlation_id, workflow_type = EXCLUDED.workflow_type, " + + "start_time = EXCLUDED.start_time, status = EXCLUDED.status, json_data = EXCLUDED.json_data"; + + TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(workflow.getStartTime()); + Timestamp startTime = Timestamp.from(Instant.from(ta)); + + queryWithTransaction( + INSERT_WORKFLOW_INDEX_SQL, + q -> + q.addParameter(workflow.getWorkflowId()) + .addParameter(workflow.getCorrelationId()) + .addParameter(workflow.getWorkflowType()) + .addParameter(startTime) + .addParameter(workflow.getStatus().toString()) + .addJsonParameter(workflow) + .executeUpdate()); + } + + @Override + public SearchResult searchWorkflowSummary( + String query, String freeText, int start, int count, List sort) { + PostgresIndexQueryBuilder queryBuilder = + new PostgresIndexQueryBuilder( + "workflow_index", query, freeText, start, count, sort); + + List results = + queryWithTransaction( + queryBuilder.getQuery(), + q -> { + queryBuilder.addParameters(q); + return q.executeAndFetch(WorkflowSummary.class); + }); + + // To avoid making a second potentially expensive query to postgres say we've + // got enough results for another page so the pagination works + int totalHits = results.size() == count ? start + count + 1 : start + results.size(); + return new SearchResult<>(totalHits, results); + } + + @Override + public void indexTask(TaskSummary task) { + String INSERT_TASK_INDEX_SQL = + "INSERT INTO task_index (task_id, task_type, task_def_name, status, start_time, update_time, workflow_type, json_data)" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?::JSONB) ON CONFLICT (task_id) " + + "DO UPDATE SET task_type = EXCLUDED.task_type, task_def_name = EXCLUDED.task_def_name, " + + "status = EXCLUDED.status, update_time = EXCLUDED.update_time, json_data = EXCLUDED.json_data"; + + TemporalAccessor updateTa = DateTimeFormatter.ISO_INSTANT.parse(task.getUpdateTime()); + Timestamp updateTime = Timestamp.from(Instant.from(updateTa)); + + TemporalAccessor startTa = DateTimeFormatter.ISO_INSTANT.parse(task.getStartTime()); + Timestamp startTime = Timestamp.from(Instant.from(startTa)); + + queryWithTransaction( + INSERT_TASK_INDEX_SQL, + q -> + q.addParameter(task.getTaskId()) + .addParameter(task.getTaskType()) + .addParameter(task.getTaskDefName()) + .addParameter(task.getStatus().toString()) + .addParameter(startTime) + .addParameter(updateTime) + .addParameter(task.getWorkflowType()) + .addJsonParameter(task) + .executeUpdate()); + } + + @Override + public SearchResult searchTaskSummary( + String query, String freeText, int start, int count, List sort) { + PostgresIndexQueryBuilder queryBuilder = + new PostgresIndexQueryBuilder("task_index", query, freeText, start, count, sort); + + List results = + queryWithTransaction( + queryBuilder.getQuery(), + q -> { + queryBuilder.addParameters(q); + return q.executeAndFetch(TaskSummary.class); + }); + + // To avoid making a second potentially expensive query to postgres say we've + // got enough results for another page so the pagination works + int totalHits = results.size() == count ? start + count + 1 : start + results.size(); + return new SearchResult<>(totalHits, results); + } + + @Override + public void addTaskExecutionLogs(List logs) { + String INSERT_LOG = + "INSERT INTO task_execution_logs (task_id, created_time, log) VALUES (?, ?, ?)"; + for (TaskExecLog log : logs) { + queryWithTransaction( + INSERT_LOG, + q -> + q.addParameter(log.getTaskId()) + .addParameter(new Timestamp(log.getCreatedTime())) + .addParameter(log.getLog()) + .executeUpdate()); + } + } + + @Override + public List getTaskExecutionLogs(String taskId) { + return queryWithTransaction( + "SELECT log, task_id, created_time FROM task_execution_logs WHERE task_id = ? ORDER BY created_time ASC", + q -> + q.addParameter(taskId) + .executeAndFetch( + rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + TaskExecLog log = new TaskExecLog(); + log.setLog(rs.getString("log")); + log.setTaskId(rs.getString("task_id")); + log.setCreatedTime( + rs.getDate("created_time").getTime()); + result.add(log); + } + return result; + })); + } + + @Override + public void setup() {} + + @Override + public CompletableFuture asyncIndexWorkflow(WorkflowSummary workflow) { + logger.info("asyncIndexWorkflow is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture asyncIndexTask(TaskSummary task) { + logger.info("asyncIndexTask is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public SearchResult searchWorkflows( + String query, String freeText, int start, int count, List sort) { + logger.info("searchWorkflows is not supported for postgres indexing"); + return null; + } + + @Override + public SearchResult searchTasks( + String query, String freeText, int start, int count, List sort) { + logger.info("searchTasks is not supported for postgres indexing"); + return null; + } + + @Override + public void removeWorkflow(String workflowId) { + logger.info("removeWorkflow is not supported for postgres indexing"); + } + + @Override + public CompletableFuture asyncRemoveWorkflow(String workflowId) { + logger.info("asyncRemoveWorkflow is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public void updateWorkflow(String workflowInstanceId, String[] keys, Object[] values) { + logger.info("updateWorkflow is not supported for postgres indexing"); + } + + @Override + public CompletableFuture asyncUpdateWorkflow( + String workflowInstanceId, String[] keys, Object[] values) { + logger.info("asyncUpdateWorkflow is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public void removeTask(String workflowId, String taskId) { + logger.info("removeTask is not supported for postgres indexing"); + } + + @Override + public CompletableFuture asyncRemoveTask(String workflowId, String taskId) { + logger.info("asyncRemoveTask is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public void updateTask(String workflowId, String taskId, String[] keys, Object[] values) { + logger.info("updateTask is not supported for postgres indexing"); + } + + @Override + public CompletableFuture asyncUpdateTask( + String workflowId, String taskId, String[] keys, Object[] values) { + logger.info("asyncUpdateTask is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public String get(String workflowInstanceId, String key) { + logger.info("get is not supported for postgres indexing"); + return null; + } + + @Override + public CompletableFuture asyncAddTaskExecutionLogs(List logs) { + logger.info("asyncAddTaskExecutionLogs is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public void addEventExecution(EventExecution eventExecution) { + logger.info("addEventExecution is not supported for postgres indexing"); + } + + @Override + public List getEventExecutions(String event) { + logger.info("getEventExecutions is not supported for postgres indexing"); + return null; + } + + @Override + public CompletableFuture asyncAddEventExecution(EventExecution eventExecution) { + logger.info("asyncAddEventExecution is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public void addMessage(String queue, Message msg) { + logger.info("addMessage is not supported for postgres indexing"); + } + + @Override + public CompletableFuture asyncAddMessage(String queue, Message message) { + logger.info("asyncAddMessage is not supported for postgres indexing"); + return CompletableFuture.completedFuture(null); + } + + @Override + public List getMessages(String queue) { + logger.info("getMessages is not supported for postgres indexing"); + return null; + } + + @Override + public List searchArchivableWorkflows(String indexName, long archiveTtlDays) { + logger.info("searchArchivableWorkflows is not supported for postgres indexing"); + return null; + } + + public long getWorkflowCount(String query, String freeText) { + logger.info("getWorkflowCount is not supported for postgres indexing"); + return 0; + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java new file mode 100644 index 000000000..c1a05dc6d --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java @@ -0,0 +1,579 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Connection; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.sql.DataSource; + +import jakarta.annotation.*; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.core.exception.ConflictException; +import com.netflix.conductor.core.exception.NotFoundException; +import com.netflix.conductor.dao.EventHandlerDAO; +import com.netflix.conductor.dao.MetadataDAO; +import com.netflix.conductor.metrics.Monitors; +import com.netflix.conductor.postgres.config.PostgresProperties; +import com.netflix.conductor.postgres.util.ExecutorsUtil; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; + +public class PostgresMetadataDAO extends PostgresBaseDAO implements MetadataDAO, EventHandlerDAO { + + private final ConcurrentHashMap taskDefCache = new ConcurrentHashMap<>(); + private static final String CLASS_NAME = PostgresMetadataDAO.class.getSimpleName(); + + private final ScheduledExecutorService scheduledExecutorService; + + public PostgresMetadataDAO( + RetryTemplate retryTemplate, + ObjectMapper objectMapper, + DataSource dataSource, + PostgresProperties properties) { + super(retryTemplate, objectMapper, dataSource); + + long cacheRefreshTime = properties.getTaskDefCacheRefreshInterval().getSeconds(); + this.scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor( + ExecutorsUtil.newNamedThreadFactory("postgres-metadata-")); + this.scheduledExecutorService.scheduleWithFixedDelay( + this::refreshTaskDefs, cacheRefreshTime, cacheRefreshTime, TimeUnit.SECONDS); + } + + @PreDestroy + public void destroy() { + try { + this.scheduledExecutorService.shutdown(); + if (scheduledExecutorService.awaitTermination(30, TimeUnit.SECONDS)) { + logger.debug("tasks completed, shutting down"); + } else { + logger.warn("Forcing shutdown after waiting for 30 seconds"); + scheduledExecutorService.shutdownNow(); + } + } catch (InterruptedException ie) { + logger.warn( + "Shutdown interrupted, invoking shutdownNow on scheduledExecutorService for refreshTaskDefs", + ie); + scheduledExecutorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + public TaskDef createTaskDef(TaskDef taskDef) { + validate(taskDef); + insertOrUpdateTaskDef(taskDef); + return taskDef; + } + + @Override + public TaskDef updateTaskDef(TaskDef taskDef) { + validate(taskDef); + insertOrUpdateTaskDef(taskDef); + return taskDef; + } + + @Override + public TaskDef getTaskDef(String name) { + Preconditions.checkNotNull(name, "TaskDef name cannot be null"); + TaskDef taskDef = taskDefCache.get(name); + if (taskDef == null) { + if (logger.isTraceEnabled()) { + logger.trace("Cache miss: {}", name); + } + taskDef = getTaskDefFromDB(name); + } + + return taskDef; + } + + @Override + public List getAllTaskDefs() { + return getWithRetriedTransactions(this::findAllTaskDefs); + } + + @Override + public void removeTaskDef(String name) { + final String DELETE_TASKDEF_QUERY = "DELETE FROM meta_task_def WHERE name = ?"; + + executeWithTransaction( + DELETE_TASKDEF_QUERY, + q -> { + if (!q.addParameter(name).executeDelete()) { + throw new NotFoundException("No such task definition"); + } + + taskDefCache.remove(name); + }); + } + + @Override + public void createWorkflowDef(WorkflowDef def) { + validate(def); + + withTransaction( + tx -> { + if (workflowExists(tx, def)) { + throw new ConflictException( + "Workflow with " + def.key() + " already exists!"); + } + + insertOrUpdateWorkflowDef(tx, def); + }); + } + + @Override + public void updateWorkflowDef(WorkflowDef def) { + validate(def); + withTransaction(tx -> insertOrUpdateWorkflowDef(tx, def)); + } + + @Override + public Optional getLatestWorkflowDef(String name) { + final String GET_LATEST_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE NAME = ? AND " + + "version = latest_version"; + + return Optional.ofNullable( + queryWithTransaction( + GET_LATEST_WORKFLOW_DEF_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(WorkflowDef.class))); + } + + @Override + public Optional getWorkflowDef(String name, int version) { + final String GET_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE NAME = ? AND version = ?"; + return Optional.ofNullable( + queryWithTransaction( + GET_WORKFLOW_DEF_QUERY, + q -> + q.addParameter(name) + .addParameter(version) + .executeAndFetchFirst(WorkflowDef.class))); + } + + @Override + public void removeWorkflowDef(String name, Integer version) { + final String DELETE_WORKFLOW_QUERY = + "DELETE from meta_workflow_def WHERE name = ? AND version = ?"; + + withTransaction( + tx -> { + // remove specified workflow + execute( + tx, + DELETE_WORKFLOW_QUERY, + q -> { + if (!q.addParameter(name).addParameter(version).executeDelete()) { + throw new NotFoundException( + String.format( + "No such workflow definition: %s version: %d", + name, version)); + } + }); + // reset latest version based on remaining rows for this workflow + Optional maxVersion = getLatestVersion(tx, name); + maxVersion.ifPresent(newVersion -> updateLatestVersion(tx, name, newVersion)); + }); + } + + public List findAll() { + final String FIND_ALL_WORKFLOW_DEF_QUERY = "SELECT DISTINCT name FROM meta_workflow_def"; + return queryWithTransaction( + FIND_ALL_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(String.class)); + } + + @Override + public List getAllWorkflowDefs() { + final String GET_ALL_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def ORDER BY name, version"; + + return queryWithTransaction( + GET_ALL_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(WorkflowDef.class)); + } + + @Override + public List getAllWorkflowDefsLatestVersions() { + final String GET_ALL_WORKFLOW_DEF_LATEST_VERSIONS_QUERY = + "SELECT json_data FROM meta_workflow_def wd WHERE wd.version = (SELECT MAX(version) FROM meta_workflow_def wd2 WHERE wd2.name = wd.name)"; + return queryWithTransaction( + GET_ALL_WORKFLOW_DEF_LATEST_VERSIONS_QUERY, + q -> q.executeAndFetch(WorkflowDef.class)); + } + + public List getAllLatest() { + final String GET_ALL_LATEST_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE version = " + "latest_version"; + + return queryWithTransaction( + GET_ALL_LATEST_WORKFLOW_DEF_QUERY, q -> q.executeAndFetch(WorkflowDef.class)); + } + + public List getAllVersions(String name) { + final String GET_ALL_VERSIONS_WORKFLOW_DEF_QUERY = + "SELECT json_data FROM meta_workflow_def WHERE name = ? " + "ORDER BY version"; + + return queryWithTransaction( + GET_ALL_VERSIONS_WORKFLOW_DEF_QUERY, + q -> q.addParameter(name).executeAndFetch(WorkflowDef.class)); + } + + @Override + public void addEventHandler(EventHandler eventHandler) { + Preconditions.checkNotNull(eventHandler.getName(), "EventHandler name cannot be null"); + + final String INSERT_EVENT_HANDLER_QUERY = + "INSERT INTO meta_event_handler (name, event, active, json_data) " + + "VALUES (?, ?, ?, ?)"; + + withTransaction( + tx -> { + if (getEventHandler(tx, eventHandler.getName()) != null) { + throw new ConflictException( + "EventHandler with name " + + eventHandler.getName() + + " already exists!"); + } + + execute( + tx, + INSERT_EVENT_HANDLER_QUERY, + q -> + q.addParameter(eventHandler.getName()) + .addParameter(eventHandler.getEvent()) + .addParameter(eventHandler.isActive()) + .addJsonParameter(eventHandler) + .executeUpdate()); + }); + } + + @Override + public void updateEventHandler(EventHandler eventHandler) { + Preconditions.checkNotNull(eventHandler.getName(), "EventHandler name cannot be null"); + + // @formatter:off + final String UPDATE_EVENT_HANDLER_QUERY = + "UPDATE meta_event_handler SET " + + "event = ?, active = ?, json_data = ?, " + + "modified_on = CURRENT_TIMESTAMP WHERE name = ?"; + // @formatter:on + + withTransaction( + tx -> { + EventHandler existing = getEventHandler(tx, eventHandler.getName()); + if (existing == null) { + throw new NotFoundException( + "EventHandler with name " + eventHandler.getName() + " not found!"); + } + + execute( + tx, + UPDATE_EVENT_HANDLER_QUERY, + q -> + q.addParameter(eventHandler.getEvent()) + .addParameter(eventHandler.isActive()) + .addJsonParameter(eventHandler) + .addParameter(eventHandler.getName()) + .executeUpdate()); + }); + } + + @Override + public void removeEventHandler(String name) { + final String DELETE_EVENT_HANDLER_QUERY = "DELETE FROM meta_event_handler WHERE name = ?"; + + withTransaction( + tx -> { + EventHandler existing = getEventHandler(tx, name); + if (existing == null) { + throw new NotFoundException( + "EventHandler with name " + name + " not found!"); + } + + execute( + tx, + DELETE_EVENT_HANDLER_QUERY, + q -> q.addParameter(name).executeDelete()); + }); + } + + @Override + public List getAllEventHandlers() { + final String READ_ALL_EVENT_HANDLER_QUERY = "SELECT json_data FROM meta_event_handler"; + return queryWithTransaction( + READ_ALL_EVENT_HANDLER_QUERY, q -> q.executeAndFetch(EventHandler.class)); + } + + @Override + public List getEventHandlersForEvent(String event, boolean activeOnly) { + final String READ_ALL_EVENT_HANDLER_BY_EVENT_QUERY = + "SELECT json_data FROM meta_event_handler WHERE event = ?"; + return queryWithTransaction( + READ_ALL_EVENT_HANDLER_BY_EVENT_QUERY, + q -> { + q.addParameter(event); + return q.executeAndFetch( + rs -> { + List handlers = new ArrayList<>(); + while (rs.next()) { + EventHandler h = readValue(rs.getString(1), EventHandler.class); + if (!activeOnly || h.isActive()) { + handlers.add(h); + } + } + + return handlers; + }); + }); + } + + /** + * Use {@link Preconditions} to check for required {@link TaskDef} fields, throwing a Runtime + * exception if validations fail. + * + * @param taskDef The {@code TaskDef} to check. + */ + private void validate(TaskDef taskDef) { + Preconditions.checkNotNull(taskDef, "TaskDef object cannot be null"); + Preconditions.checkNotNull(taskDef.getName(), "TaskDef name cannot be null"); + } + + /** + * Use {@link Preconditions} to check for required {@link WorkflowDef} fields, throwing a + * Runtime exception if validations fail. + * + * @param def The {@code WorkflowDef} to check. + */ + private void validate(WorkflowDef def) { + Preconditions.checkNotNull(def, "WorkflowDef object cannot be null"); + Preconditions.checkNotNull(def.getName(), "WorkflowDef name cannot be null"); + } + + /** + * Retrieve a {@link EventHandler} by {@literal name}. + * + * @param connection The {@link Connection} to use for queries. + * @param name The {@code EventHandler} name to look for. + * @return {@literal null} if nothing is found, otherwise the {@code EventHandler}. + */ + private EventHandler getEventHandler(Connection connection, String name) { + final String READ_ONE_EVENT_HANDLER_QUERY = + "SELECT json_data FROM meta_event_handler WHERE name = ?"; + + return query( + connection, + READ_ONE_EVENT_HANDLER_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(EventHandler.class)); + } + + /** + * Check if a {@link WorkflowDef} with the same {@literal name} and {@literal version} already + * exist. + * + * @param connection The {@link Connection} to use for queries. + * @param def The {@code WorkflowDef} to check for. + * @return {@literal true} if a {@code WorkflowDef} already exists with the same values. + */ + private Boolean workflowExists(Connection connection, WorkflowDef def) { + final String CHECK_WORKFLOW_DEF_EXISTS_QUERY = + "SELECT COUNT(*) FROM meta_workflow_def WHERE name = ? AND " + "version = ?"; + + return query( + connection, + CHECK_WORKFLOW_DEF_EXISTS_QUERY, + q -> q.addParameter(def.getName()).addParameter(def.getVersion()).exists()); + } + + /** + * Return the latest version that exists for the provided {@code name}. + * + * @param tx The {@link Connection} to use for queries. + * @param name The {@code name} to check for. + * @return {@code Optional.empty()} if no versions exist, otherwise the max {@link + * WorkflowDef#getVersion} found. + */ + private Optional getLatestVersion(Connection tx, String name) { + final String GET_LATEST_WORKFLOW_DEF_VERSION = + "SELECT max(version) AS version FROM meta_workflow_def WHERE " + "name = ?"; + + Integer val = + query( + tx, + GET_LATEST_WORKFLOW_DEF_VERSION, + q -> { + q.addParameter(name); + return q.executeAndFetch( + rs -> { + if (!rs.next()) { + return null; + } + + return rs.getInt(1); + }); + }); + + return Optional.ofNullable(val); + } + + /** + * Update the latest version for the workflow with name {@code WorkflowDef} to the version + * provided in {@literal version}. + * + * @param tx The {@link Connection} to use for queries. + * @param name Workflow def name to update + * @param version The new latest {@code version} value. + */ + private void updateLatestVersion(Connection tx, String name, int version) { + final String UPDATE_WORKFLOW_DEF_LATEST_VERSION_QUERY = + "UPDATE meta_workflow_def SET latest_version = ? " + "WHERE name = ?"; + + execute( + tx, + UPDATE_WORKFLOW_DEF_LATEST_VERSION_QUERY, + q -> q.addParameter(version).addParameter(name).executeUpdate()); + } + + private void insertOrUpdateWorkflowDef(Connection tx, WorkflowDef def) { + final String INSERT_WORKFLOW_DEF_QUERY = + "INSERT INTO meta_workflow_def (name, version, json_data) VALUES (?," + " ?, ?)"; + + Optional version = getLatestVersion(tx, def.getName()); + if (!workflowExists(tx, def)) { + execute( + tx, + INSERT_WORKFLOW_DEF_QUERY, + q -> + q.addParameter(def.getName()) + .addParameter(def.getVersion()) + .addJsonParameter(def) + .executeUpdate()); + } else { + // @formatter:off + final String UPDATE_WORKFLOW_DEF_QUERY = + "UPDATE meta_workflow_def " + + "SET json_data = ?, modified_on = CURRENT_TIMESTAMP " + + "WHERE name = ? AND version = ?"; + // @formatter:on + + execute( + tx, + UPDATE_WORKFLOW_DEF_QUERY, + q -> + q.addJsonParameter(def) + .addParameter(def.getName()) + .addParameter(def.getVersion()) + .executeUpdate()); + } + int maxVersion = def.getVersion(); + if (version.isPresent() && version.get() > def.getVersion()) { + maxVersion = version.get(); + } + + updateLatestVersion(tx, def.getName(), maxVersion); + } + + /** + * Query persistence for all defined {@link TaskDef} data, and cache it in {@link + * #taskDefCache}. + */ + private void refreshTaskDefs() { + try { + withTransaction( + tx -> { + Map map = new HashMap<>(); + findAllTaskDefs(tx).forEach(taskDef -> map.put(taskDef.getName(), taskDef)); + + synchronized (taskDefCache) { + taskDefCache.clear(); + taskDefCache.putAll(map); + } + + if (logger.isTraceEnabled()) { + logger.trace("Refreshed {} TaskDefs", taskDefCache.size()); + } + }); + } catch (Exception e) { + Monitors.error(CLASS_NAME, "refreshTaskDefs"); + logger.error("refresh TaskDefs failed ", e); + } + } + + /** + * Query persistence for all defined {@link TaskDef} data. + * + * @param tx The {@link Connection} to use for queries. + * @return A new {@code List} with all the {@code TaskDef} data that was retrieved. + */ + private List findAllTaskDefs(Connection tx) { + final String READ_ALL_TASKDEF_QUERY = "SELECT json_data FROM meta_task_def"; + + return query(tx, READ_ALL_TASKDEF_QUERY, q -> q.executeAndFetch(TaskDef.class)); + } + + /** + * Explicitly retrieves a {@link TaskDef} from persistence, avoiding {@link #taskDefCache}. + * + * @param name The name of the {@code TaskDef} to query for. + * @return {@literal null} if nothing is found, otherwise the {@code TaskDef}. + */ + private TaskDef getTaskDefFromDB(String name) { + final String READ_ONE_TASKDEF_QUERY = "SELECT json_data FROM meta_task_def WHERE name = ?"; + + return queryWithTransaction( + READ_ONE_TASKDEF_QUERY, + q -> q.addParameter(name).executeAndFetchFirst(TaskDef.class)); + } + + private String insertOrUpdateTaskDef(TaskDef taskDef) { + final String UPDATE_TASKDEF_QUERY = + "UPDATE meta_task_def SET json_data = ?, modified_on = CURRENT_TIMESTAMP WHERE name = ?"; + + final String INSERT_TASKDEF_QUERY = + "INSERT INTO meta_task_def (name, json_data) VALUES (?, ?)"; + + return getWithRetriedTransactions( + tx -> { + execute( + tx, + UPDATE_TASKDEF_QUERY, + update -> { + int result = + update.addJsonParameter(taskDef) + .addParameter(taskDef.getName()) + .executeUpdate(); + if (result == 0) { + execute( + tx, + INSERT_TASKDEF_QUERY, + insert -> + insert.addParameter(taskDef.getName()) + .addJsonParameter(taskDef) + .executeUpdate()); + } + }); + + taskDefCache.put(taskDef.getName(), taskDef); + return taskDef.getName(); + }); + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java new file mode 100644 index 000000000..76275722b --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java @@ -0,0 +1,501 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Connection; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import jakarta.annotation.*; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.dao.QueueDAO; +import com.netflix.conductor.postgres.util.ExecutorsUtil; +import com.netflix.conductor.postgres.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.Uninterruptibles; + +public class PostgresQueueDAO extends PostgresBaseDAO implements QueueDAO { + + private static final Long UNACK_SCHEDULE_MS = 60_000L; + + private final ScheduledExecutorService scheduledExecutorService; + + public PostgresQueueDAO( + RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { + super(retryTemplate, objectMapper, dataSource); + + this.scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor( + ExecutorsUtil.newNamedThreadFactory("postgres-queue-")); + this.scheduledExecutorService.scheduleAtFixedRate( + this::processAllUnacks, + UNACK_SCHEDULE_MS, + UNACK_SCHEDULE_MS, + TimeUnit.MILLISECONDS); + logger.debug("{} is ready to serve", PostgresQueueDAO.class.getName()); + } + + @PreDestroy + public void destroy() { + try { + this.scheduledExecutorService.shutdown(); + if (scheduledExecutorService.awaitTermination(30, TimeUnit.SECONDS)) { + logger.debug("tasks completed, shutting down"); + } else { + logger.warn("Forcing shutdown after waiting for 30 seconds"); + scheduledExecutorService.shutdownNow(); + } + } catch (InterruptedException ie) { + logger.warn( + "Shutdown interrupted, invoking shutdownNow on scheduledExecutorService for processAllUnacks", + ie); + scheduledExecutorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + public void push(String queueName, String messageId, long offsetTimeInSecond) { + push(queueName, messageId, 0, offsetTimeInSecond); + } + + @Override + public void push(String queueName, String messageId, int priority, long offsetTimeInSecond) { + withTransaction( + tx -> pushMessage(tx, queueName, messageId, null, priority, offsetTimeInSecond)); + } + + @Override + public void push(String queueName, List messages) { + withTransaction( + tx -> + messages.forEach( + message -> + pushMessage( + tx, + queueName, + message.getId(), + message.getPayload(), + message.getPriority(), + 0))); + } + + @Override + public boolean pushIfNotExists(String queueName, String messageId, long offsetTimeInSecond) { + return pushIfNotExists(queueName, messageId, 0, offsetTimeInSecond); + } + + @Override + public boolean pushIfNotExists( + String queueName, String messageId, int priority, long offsetTimeInSecond) { + return getWithRetriedTransactions( + tx -> { + if (!existsMessage(tx, queueName, messageId)) { + pushMessage(tx, queueName, messageId, null, priority, offsetTimeInSecond); + return true; + } + return false; + }); + } + + @Override + public List pop(String queueName, int count, int timeout) { + return pollMessages(queueName, count, timeout).stream() + .map(Message::getId) + .collect(Collectors.toList()); + } + + @Override + public List pollMessages(String queueName, int count, int timeout) { + if (timeout < 1) { + List messages = + getWithTransactionWithOutErrorPropagation( + tx -> popMessages(tx, queueName, count, timeout)); + if (messages == null) { + return new ArrayList<>(); + } + return messages; + } + + long start = System.currentTimeMillis(); + final List messages = new ArrayList<>(); + + while (true) { + List messagesSlice = + getWithTransactionWithOutErrorPropagation( + tx -> popMessages(tx, queueName, count - messages.size(), timeout)); + if (messagesSlice == null) { + logger.warn( + "Unable to poll {} messages from {} due to tx conflict, only {} popped", + count, + queueName, + messages.size()); + // conflict could have happened, returned messages popped so far + return messages; + } + + messages.addAll(messagesSlice); + if (messages.size() >= count || ((System.currentTimeMillis() - start) > timeout)) { + return messages; + } + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + } + } + + @Override + public void remove(String queueName, String messageId) { + withTransaction(tx -> removeMessage(tx, queueName, messageId)); + } + + @Override + public int getSize(String queueName) { + final String GET_QUEUE_SIZE = "SELECT COUNT(*) FROM queue_message WHERE queue_name = ?"; + return queryWithTransaction( + GET_QUEUE_SIZE, q -> ((Long) q.addParameter(queueName).executeCount()).intValue()); + } + + @Override + public boolean ack(String queueName, String messageId) { + return getWithRetriedTransactions(tx -> removeMessage(tx, queueName, messageId)); + } + + @Override + public boolean setUnackTimeout(String queueName, String messageId, long unackTimeout) { + long updatedOffsetTimeInSecond = unackTimeout / 1000; + + final String UPDATE_UNACK_TIMEOUT = + "UPDATE queue_message SET offset_time_seconds = ?, deliver_on = (current_timestamp + (? ||' seconds')::interval) WHERE queue_name = ? AND message_id = ?"; + + return queryWithTransaction( + UPDATE_UNACK_TIMEOUT, + q -> + q.addParameter(updatedOffsetTimeInSecond) + .addParameter(updatedOffsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate()) + == 1; + } + + @Override + public void flush(String queueName) { + final String FLUSH_QUEUE = "DELETE FROM queue_message WHERE queue_name = ?"; + executeWithTransaction(FLUSH_QUEUE, q -> q.addParameter(queueName).executeDelete()); + } + + @Override + public Map queuesDetail() { + final String GET_QUEUES_DETAIL = + "SELECT queue_name, (SELECT count(*) FROM queue_message WHERE popped = false AND queue_name = q.queue_name) AS size FROM queue q FOR SHARE SKIP LOCKED"; + return queryWithTransaction( + GET_QUEUES_DETAIL, + q -> + q.executeAndFetch( + rs -> { + Map detail = Maps.newHashMap(); + while (rs.next()) { + String queueName = rs.getString("queue_name"); + Long size = rs.getLong("size"); + detail.put(queueName, size); + } + return detail; + })); + } + + @Override + public Map>> queuesDetailVerbose() { + // @formatter:off + final String GET_QUEUES_DETAIL_VERBOSE = + "SELECT queue_name, \n" + + " (SELECT count(*) FROM queue_message WHERE popped = false AND queue_name = q.queue_name) AS size,\n" + + " (SELECT count(*) FROM queue_message WHERE popped = true AND queue_name = q.queue_name) AS uacked \n" + + "FROM queue q FOR SHARE SKIP LOCKED"; + // @formatter:on + + return queryWithTransaction( + GET_QUEUES_DETAIL_VERBOSE, + q -> + q.executeAndFetch( + rs -> { + Map>> result = + Maps.newHashMap(); + while (rs.next()) { + String queueName = rs.getString("queue_name"); + Long size = rs.getLong("size"); + Long queueUnacked = rs.getLong("uacked"); + result.put( + queueName, + ImmutableMap.of( + "a", + ImmutableMap + .of( // sharding not implemented, + // returning only + // one shard with all the + // info + "size", + size, + "uacked", + queueUnacked))); + } + return result; + })); + } + + /** + * Un-pop all un-acknowledged messages for all queues. + * + * @since 1.11.6 + */ + public void processAllUnacks() { + logger.trace("processAllUnacks started"); + + getWithRetriedTransactions( + tx -> { + String LOCK_TASKS = + "SELECT queue_name, message_id FROM queue_message WHERE popped = true AND (deliver_on + (60 ||' seconds')::interval) < current_timestamp limit 1000 FOR UPDATE SKIP LOCKED"; + + List messages = + query( + tx, + LOCK_TASKS, + p -> + p.executeAndFetch( + rs -> { + List results = + new ArrayList(); + while (rs.next()) { + QueueMessage qm = new QueueMessage(); + qm.queueName = + rs.getString("queue_name"); + qm.messageId = + rs.getString("message_id"); + results.add(qm); + } + return results; + })); + + if (messages.size() == 0) { + return 0; + } + + Map> queueMessageMap = new HashMap>(); + for (QueueMessage qm : messages) { + if (!queueMessageMap.containsKey(qm.queueName)) { + queueMessageMap.put(qm.queueName, new ArrayList()); + } + queueMessageMap.get(qm.queueName).add(qm.messageId); + } + + int totalUnacked = 0; + for (String queueName : queueMessageMap.keySet()) { + Integer unacked = 0; + ; + try { + final List msgIds = queueMessageMap.get(queueName); + final String UPDATE_POPPED = + String.format( + "UPDATE queue_message SET popped = false WHERE queue_name = ? and message_id IN (%s)", + Query.generateInBindings(msgIds.size())); + + unacked = + query( + tx, + UPDATE_POPPED, + q -> + q.addParameter(queueName) + .addParameters(msgIds) + .executeUpdate()); + } catch (Exception e) { + e.printStackTrace(); + } + totalUnacked += unacked; + logger.debug("Unacked {} messages from all queues", unacked); + } + + if (totalUnacked > 0) { + logger.debug("Unacked {} messages from all queues", totalUnacked); + } + return totalUnacked; + }); + } + + @Override + public void processUnacks(String queueName) { + final String PROCESS_UNACKS = + "UPDATE queue_message SET popped = false WHERE queue_name = ? AND popped = true AND (current_timestamp - (60 ||' seconds')::interval) > deliver_on"; + executeWithTransaction(PROCESS_UNACKS, q -> q.addParameter(queueName).executeUpdate()); + } + + @Override + public boolean resetOffsetTime(String queueName, String messageId) { + long offsetTimeInSecond = 0; // Reset to 0 + final String SET_OFFSET_TIME = + "UPDATE queue_message SET offset_time_seconds = ?, deliver_on = (current_timestamp + (? ||' seconds')::interval) \n" + + "WHERE queue_name = ? AND message_id = ?"; + + return queryWithTransaction( + SET_OFFSET_TIME, + q -> + q.addParameter(offsetTimeInSecond) + .addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate() + == 1); + } + + private boolean existsMessage(Connection connection, String queueName, String messageId) { + final String EXISTS_MESSAGE = + "SELECT EXISTS(SELECT 1 FROM queue_message WHERE queue_name = ? AND message_id = ?) FOR SHARE"; + return query( + connection, + EXISTS_MESSAGE, + q -> q.addParameter(queueName).addParameter(messageId).exists()); + } + + private void pushMessage( + Connection connection, + String queueName, + String messageId, + String payload, + Integer priority, + long offsetTimeInSecond) { + + createQueueIfNotExists(connection, queueName); + + String UPDATE_MESSAGE = + "UPDATE queue_message SET payload=?, deliver_on=(current_timestamp + (? ||' seconds')::interval) WHERE queue_name = ? AND message_id = ?"; + int rowsUpdated = + query( + connection, + UPDATE_MESSAGE, + q -> + q.addParameter(payload) + .addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .executeUpdate()); + + if (rowsUpdated == 0) { + String PUSH_MESSAGE = + "INSERT INTO queue_message (deliver_on, queue_name, message_id, priority, offset_time_seconds, payload) VALUES ((current_timestamp + (? ||' seconds')::interval), ?,?,?,?,?) ON CONFLICT (queue_name,message_id) DO UPDATE SET payload=excluded.payload, deliver_on=excluded.deliver_on"; + execute( + connection, + PUSH_MESSAGE, + q -> + q.addParameter(offsetTimeInSecond) + .addParameter(queueName) + .addParameter(messageId) + .addParameter(priority) + .addParameter(offsetTimeInSecond) + .addParameter(payload) + .executeUpdate()); + } + } + + private boolean removeMessage(Connection connection, String queueName, String messageId) { + final String REMOVE_MESSAGE = + "DELETE FROM queue_message WHERE queue_name = ? AND message_id = ?"; + return query( + connection, + REMOVE_MESSAGE, + q -> q.addParameter(queueName).addParameter(messageId).executeDelete()); + } + + private List peekMessages(Connection connection, String queueName, int count) { + if (count < 1) { + return Collections.emptyList(); + } + + final String PEEK_MESSAGES = + "SELECT message_id, priority, payload FROM queue_message WHERE queue_name = ? AND popped = false AND deliver_on <= (current_timestamp + (1000 ||' microseconds')::interval) ORDER BY priority DESC, deliver_on, created_on LIMIT ? FOR UPDATE SKIP LOCKED"; + + return query( + connection, + PEEK_MESSAGES, + p -> + p.addParameter(queueName) + .addParameter(count) + .executeAndFetch( + rs -> { + List results = new ArrayList<>(); + while (rs.next()) { + Message m = new Message(); + m.setId(rs.getString("message_id")); + m.setPriority(rs.getInt("priority")); + m.setPayload(rs.getString("payload")); + results.add(m); + } + return results; + })); + } + + private List popMessages( + Connection connection, String queueName, int count, int timeout) { + List messages = peekMessages(connection, queueName, count); + + if (messages.isEmpty()) { + return messages; + } + + List poppedMessages = new ArrayList<>(); + for (Message message : messages) { + final String POP_MESSAGE = + "UPDATE queue_message SET popped = true WHERE queue_name = ? AND message_id = ? AND popped = false"; + int result = + query( + connection, + POP_MESSAGE, + q -> + q.addParameter(queueName) + .addParameter(message.getId()) + .executeUpdate()); + + if (result == 1) { + poppedMessages.add(message); + } + } + return poppedMessages; + } + + @Override + public boolean containsMessage(String queueName, String messageId) { + return getWithRetriedTransactions(tx -> existsMessage(tx, queueName, messageId)); + } + + private void createQueueIfNotExists(Connection connection, String queueName) { + logger.trace("Creating new queue '{}'", queueName); + final String EXISTS_QUEUE = + "SELECT EXISTS(SELECT 1 FROM queue WHERE queue_name = ?) FOR SHARE"; + boolean exists = query(connection, EXISTS_QUEUE, q -> q.addParameter(queueName).exists()); + if (!exists) { + final String CREATE_QUEUE = + "INSERT INTO queue (queue_name) VALUES (?) ON CONFLICT (queue_name) DO NOTHING"; + execute(connection, CREATE_QUEUE, q -> q.addParameter(queueName).executeUpdate()); + } + } + + private class QueueMessage { + public String queueName; + public String messageId; + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java new file mode 100644 index 000000000..6b9593c7a --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java @@ -0,0 +1,25 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.SQLException; + +/** + * Functional interface for {@link Query} executions with no expected result. + * + * @author mustafa + */ +@FunctionalInterface +public interface ExecuteFunction { + + void apply(Query query) throws SQLException; +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java new file mode 100644 index 000000000..4a0ee2c2c --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java @@ -0,0 +1,36 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class ExecutorsUtil { + + private ExecutorsUtil() {} + + public static ThreadFactory newNamedThreadFactory(final String threadNamePrefix) { + return new ThreadFactory() { + + private final AtomicInteger counter = new AtomicInteger(); + + @SuppressWarnings("NullableProblems") + @Override + public Thread newThread(Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName(threadNamePrefix + counter.getAndIncrement()); + return thread; + } + }; + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java new file mode 100644 index 000000000..2d05d4267 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java @@ -0,0 +1,32 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.util.function.Supplier; + +/** Functional class to support the lazy execution of a String result. */ +public class LazyToString { + + private final Supplier supplier; + + /** + * @param supplier Supplier to execute when {@link #toString()} is called. + */ + public LazyToString(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public String toString() { + return supplier.get(); + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java new file mode 100644 index 000000000..256970fd9 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java @@ -0,0 +1,220 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.SQLException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +public class PostgresIndexQueryBuilder { + + private final String table; + private final String freeText; + private final int start; + private final int count; + private final List sort; + private final List conditions = new ArrayList<>(); + + private static final String[] VALID_FIELDS = { + "workflow_id", + "correlation_id", + "workflow_type", + "start_time", + "status", + "task_id", + "task_type", + "task_def_name", + "update_time", + "json_data", + "to_tsvector(json_data::text)" + }; + + private static final String[] VALID_SORT_ORDER = {"ASC", "DESC"}; + + private static class Condition { + private String attribute; + private String operator; + private List values; + private final String CONDITION_REGEX = "([a-zA-Z]+)\\s?(=|>|<|IN)\\s?(.*)"; + + public Condition() {} + + public Condition(String query) { + Pattern conditionRegex = Pattern.compile(CONDITION_REGEX); + Matcher conditionMatcher = conditionRegex.matcher(query); + if (conditionMatcher.find()) { + String[] valueArr = conditionMatcher.group(3).replaceAll("[\"()]", "").split(","); + ArrayList values = new ArrayList<>(Arrays.asList(valueArr)); + this.attribute = camelToSnake(conditionMatcher.group(1)); + this.values = values; + this.operator = getOperator(conditionMatcher.group(2)); + if (this.attribute.endsWith("_time")) { + values.set(0, millisToUtc(values.get(0))); + } + } + } + + public String getQueryFragment() { + if (operator.equals("IN")) { + return attribute + " = ANY(?)"; + } else if (operator.equals("@@")) { + return attribute + " @@ to_tsquery(?)"; + } else if (operator.equals("@>")) { + return attribute + " @> ?::JSONB"; + } else { + if (attribute.endsWith("_time")) { + return attribute + " " + operator + " ?::TIMESTAMPTZ"; + } else { + return attribute + " " + operator + " ?"; + } + } + } + + private String getOperator(String op) { + if (op.equals("IN") && values.size() == 1) { + return "="; + } + return op; + } + + public void addParameter(Query q) throws SQLException { + if (values.size() > 1) { + q.addParameter(values); + } else { + q.addParameter(values.get(0)); + } + } + + private String millisToUtc(String millis) { + Long startTimeMilli = Long.parseLong(millis); + ZonedDateTime startDate = + ZonedDateTime.ofInstant(Instant.ofEpochMilli(startTimeMilli), ZoneOffset.UTC); + return DateTimeFormatter.ISO_DATE_TIME.format(startDate); + } + + private boolean isValid() { + return Arrays.asList(VALID_FIELDS).contains(attribute); + } + + public void setAttribute(String attribute) { + this.attribute = attribute; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + public void setValues(List values) { + this.values = values; + } + } + + public PostgresIndexQueryBuilder( + String table, String query, String freeText, int start, int count, List sort) { + this.table = table; + this.freeText = freeText; + this.start = start; + this.count = count; + this.sort = sort; + this.parseQuery(query); + this.parseFreeText(freeText); + } + + public String getQuery() { + String queryString = ""; + List validConditions = + conditions.stream().filter(c -> c.isValid()).collect(Collectors.toList()); + if (validConditions.size() > 0) { + queryString = + " WHERE " + + String.join( + " AND ", + validConditions.stream() + .map(c -> c.getQueryFragment()) + .collect(Collectors.toList())); + } + return "SELECT json_data::TEXT FROM " + + table + + queryString + + getSort() + + " LIMIT ? OFFSET ?"; + } + + public void addParameters(Query q) throws SQLException { + for (Condition condition : conditions) { + condition.addParameter(q); + } + q.addParameter(count); + q.addParameter(start); + } + + private void parseQuery(String query) { + if (!StringUtils.isEmpty(query)) { + for (String s : query.split(" AND ")) { + conditions.add(new Condition(s)); + } + Collections.sort(conditions, Comparator.comparing(Condition::getQueryFragment)); + } + } + + private void parseFreeText(String freeText) { + if (!StringUtils.isEmpty(freeText) && !freeText.equals("*")) { + if (freeText.startsWith("{") && freeText.endsWith("}")) { + Condition cond = new Condition(); + cond.setAttribute("json_data"); + cond.setOperator("@>"); + String[] values = {freeText}; + cond.setValues(Arrays.asList(values)); + conditions.add(cond); + } else { + Condition cond = new Condition(); + cond.setAttribute("to_tsvector(json_data::text)"); + cond.setOperator("@@"); + String[] values = {freeText}; + cond.setValues(Arrays.asList(values)); + conditions.add(cond); + } + } + } + + private String getSort() { + ArrayList sortConds = new ArrayList<>(); + for (String s : sort) { + String[] splitCond = s.split(":"); + if (splitCond.length == 2) { + String attribute = camelToSnake(splitCond[0]); + String order = splitCond[1].toUpperCase(); + if (Arrays.asList(VALID_FIELDS).contains(attribute) + && Arrays.asList(VALID_SORT_ORDER).contains(order)) { + sortConds.add(attribute + " " + order); + } + } + } + + if (sortConds.size() > 0) { + return " ORDER BY " + String.join(", ", sortConds); + } + return ""; + } + + private static String camelToSnake(String camel) { + return camel.replaceAll("\\B([A-Z])", "_$1").toLowerCase(); + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java new file mode 100644 index 000000000..5d42a8b75 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java @@ -0,0 +1,647 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.io.IOException; +import java.sql.*; +import java.sql.Date; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.exception.NonTransientException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Represents a {@link PreparedStatement} that is wrapped with convenience methods and utilities. + * + *

This class simulates a parameter building pattern and all {@literal addParameter(*)} methods + * must be called in the proper order of their expected binding sequence. + * + * @author mustafa + */ +public class Query implements AutoCloseable { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The {@link ObjectMapper} instance to use for serializing/deserializing JSON. */ + protected final ObjectMapper objectMapper; + + /** The initial supplied query String that was used to prepare {@link #statement}. */ + private final String rawQuery; + + /** + * Parameter index for the {@code ResultSet#set*(*)} methods, gets incremented every time a + * parameter is added to the {@code PreparedStatement} {@link #statement}. + */ + private final AtomicInteger index = new AtomicInteger(1); + + /** The {@link PreparedStatement} that will be managed and executed by this class. */ + private final PreparedStatement statement; + + private final Connection connection; + + public Query(ObjectMapper objectMapper, Connection connection, String query) { + this.rawQuery = query; + this.objectMapper = objectMapper; + this.connection = connection; + + try { + this.statement = connection.prepareStatement(query); + } catch (SQLException ex) { + throw new NonTransientException( + "Cannot prepare statement for query: " + ex.getMessage(), ex); + } + } + + /** + * Generate a String with {@literal count} number of '?' placeholders for {@link + * PreparedStatement} queries. + * + * @param count The number of '?' chars to generate. + * @return a comma delimited string of {@literal count} '?' binding placeholders. + */ + public static String generateInBindings(int count) { + String[] questions = new String[count]; + for (int i = 0; i < count; i++) { + questions[i] = "?"; + } + + return String.join(", ", questions); + } + + public Query addParameter(final String value) { + return addParameterInternal((ps, idx) -> ps.setString(idx, value)); + } + + public Query addParameter(final List value) throws SQLException { + String[] valueStringArray = value.toArray(new String[0]); + Array valueArray = this.connection.createArrayOf("VARCHAR", valueStringArray); + return addParameterInternal((ps, idx) -> ps.setArray(idx, valueArray)); + } + + public Query addParameter(final int value) { + return addParameterInternal((ps, idx) -> ps.setInt(idx, value)); + } + + public Query addParameter(final boolean value) { + return addParameterInternal(((ps, idx) -> ps.setBoolean(idx, value))); + } + + public Query addParameter(final long value) { + return addParameterInternal((ps, idx) -> ps.setLong(idx, value)); + } + + public Query addParameter(final double value) { + return addParameterInternal((ps, idx) -> ps.setDouble(idx, value)); + } + + public Query addParameter(Date date) { + return addParameterInternal((ps, idx) -> ps.setDate(idx, date)); + } + + public Query addParameter(Timestamp timestamp) { + return addParameterInternal((ps, idx) -> ps.setTimestamp(idx, timestamp)); + } + + /** + * Serializes {@literal value} to a JSON string for persistence. + * + * @param value The value to serialize. + * @return {@literal this} + */ + public Query addJsonParameter(Object value) { + return addParameter(toJson(value)); + } + + /** + * Bind the given {@link java.util.Date} to the PreparedStatement as a {@link Date}. + * + * @param date The {@literal java.util.Date} to bind. + * @return {@literal this} + */ + public Query addDateParameter(java.util.Date date) { + return addParameter(new Date(date.getTime())); + } + + /** + * Bind the given {@link java.util.Date} to the PreparedStatement as a {@link Timestamp}. + * + * @param date The {@literal java.util.Date} to bind. + * @return {@literal this} + */ + public Query addTimestampParameter(java.util.Date date) { + return addParameter(new Timestamp(date.getTime())); + } + + /** + * Bind the given epoch millis to the PreparedStatement as a {@link Timestamp}. + * + * @param epochMillis The epoch ms to create a new {@literal Timestamp} from. + * @return {@literal this} + */ + public Query addTimestampParameter(long epochMillis) { + return addParameter(new Timestamp(epochMillis)); + } + + /** + * Add a collection of primitive values at once, in the order of the collection. + * + * @param values The values to bind to the prepared statement. + * @return {@literal this} + * @throws IllegalArgumentException If a non-primitive/unsupported type is encountered in the + * collection. + * @see #addParameters(Object...) + */ + public Query addParameters(Collection values) { + return addParameters(values.toArray()); + } + + /** + * Add many primitive values at once. + * + * @param values The values to bind to the prepared statement. + * @return {@literal this} + * @throws IllegalArgumentException If a non-primitive/unsupported type is encountered. + */ + public Query addParameters(Object... values) { + for (Object v : values) { + if (v instanceof String) { + addParameter((String) v); + } else if (v instanceof Integer) { + addParameter((Integer) v); + } else if (v instanceof Long) { + addParameter((Long) v); + } else if (v instanceof Double) { + addParameter((Double) v); + } else if (v instanceof Boolean) { + addParameter((Boolean) v); + } else if (v instanceof Date) { + addParameter((Date) v); + } else if (v instanceof Timestamp) { + addParameter((Timestamp) v); + } else { + throw new IllegalArgumentException( + "Type " + + v.getClass().getName() + + " is not supported by automatic property assignment"); + } + } + + return this; + } + + /** + * Utility method for evaluating the prepared statement as a query to check the existence of a + * record using a numeric count or boolean return value. + * + *

The {@link #rawQuery} provided must result in a {@link Number} or {@link Boolean} result. + * + * @return {@literal true} If a count query returned more than 0 or an exists query returns + * {@literal true}. + * @throws NonTransientException If an unexpected return type cannot be evaluated to a {@code + * Boolean} result. + */ + public boolean exists() { + Object val = executeScalar(); + if (null == val) { + return false; + } + + if (val instanceof Number) { + return convertLong(val) > 0; + } + + if (val instanceof Boolean) { + return (Boolean) val; + } + + if (val instanceof String) { + return convertBoolean(val); + } + + throw new NonTransientException( + "Expected a Numeric or Boolean scalar return value from the query, received " + + val.getClass().getName()); + } + + /** + * Convenience method for executing delete statements. + * + * @return {@literal true} if the statement affected 1 or more rows. + * @see #executeUpdate() + */ + public boolean executeDelete() { + int count = executeUpdate(); + if (count > 1) { + logger.trace("Removed {} row(s) for query {}", count, rawQuery); + } + + return count > 0; + } + + /** + * Convenience method for executing statements that return a single numeric value, typically + * {@literal SELECT COUNT...} style queries. + * + * @return The result of the query as a {@literal long}. + */ + public long executeCount() { + return executeScalar(Long.class); + } + + /** + * @return The result of {@link PreparedStatement#executeUpdate()} + */ + public int executeUpdate() { + try { + + Long start = null; + if (logger.isTraceEnabled()) { + start = System.currentTimeMillis(); + } + + final int val = this.statement.executeUpdate(); + + if (null != start && logger.isTraceEnabled()) { + long end = System.currentTimeMillis(); + logger.trace("[{}ms] {}: {}", (end - start), val, rawQuery); + } + + return val; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute a query from the PreparedStatement and return the ResultSet. + * + *

NOTE: The returned ResultSet must be closed/managed by the calling methods. + * + * @return {@link PreparedStatement#executeQuery()} + * @throws NonTransientException If any SQL errors occur. + */ + public ResultSet executeQuery() { + Long start = null; + if (logger.isTraceEnabled()) { + start = System.currentTimeMillis(); + } + + try { + return this.statement.executeQuery(); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } finally { + if (null != start && logger.isTraceEnabled()) { + long end = System.currentTimeMillis(); + logger.trace("[{}ms] {}", (end - start), rawQuery); + } + } + } + + /** + * @return The single result of the query as an Object. + */ + public Object executeScalar() { + try (ResultSet rs = executeQuery()) { + if (!rs.next()) { + return null; + } + return rs.getObject(1); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the PreparedStatement and return a single 'primitive' value from the ResultSet. + * + * @param returnType The type to return. + * @param The type parameter to return a List of. + * @return A single result from the execution of the statement, as a type of {@literal + * returnType}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public V executeScalar(Class returnType) { + try (ResultSet rs = executeQuery()) { + if (!rs.next()) { + Object value = null; + if (Integer.class == returnType) { + value = 0; + } else if (Long.class == returnType) { + value = 0L; + } else if (Boolean.class == returnType) { + value = false; + } + return returnType.cast(value); + } else { + return getScalarFromResultSet(rs, returnType); + } + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the PreparedStatement and return a List of 'primitive' values from the ResultSet. + * + * @param returnType The type Class return a List of. + * @param The type parameter to return a List of. + * @return A {@code List}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public List executeScalarList(Class returnType) { + try (ResultSet rs = executeQuery()) { + List values = new ArrayList<>(); + while (rs.next()) { + values.add(getScalarFromResultSet(rs, returnType)); + } + return values; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the statement and return only the first record from the result set. + * + * @param returnType The Class to return. + * @param The type parameter. + * @return An instance of {@literal } from the result set. + */ + public V executeAndFetchFirst(Class returnType) { + Object o = executeScalar(); + if (null == o) { + return null; + } + return convert(o, returnType); + } + + /** + * Execute the PreparedStatement and return a List of {@literal returnType} values from the + * ResultSet. + * + * @param returnType The type Class return a List of. + * @param The type parameter to return a List of. + * @return A {@code List}. + * @throws NonTransientException {@literal returnType} is unsupported, cannot be cast to from + * the result, or any SQL errors occur. + */ + public List executeAndFetch(Class returnType) { + try (ResultSet rs = executeQuery()) { + List list = new ArrayList<>(); + while (rs.next()) { + list.add(convert(rs.getObject(1), returnType)); + } + return list; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the PreparedStatement and return a List of {@literal Map} values from the ResultSet. + * + * @return A {@code List}. + * @throws SQLException if any SQL errors occur. + * @throws NonTransientException if any SQL errors occur. + */ + public List> executeAndFetchMap() { + try (ResultSet rs = executeQuery()) { + List> result = new ArrayList<>(); + ResultSetMetaData metadata = rs.getMetaData(); + int columnCount = metadata.getColumnCount(); + while (rs.next()) { + HashMap row = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(metadata.getColumnLabel(i), rs.getObject(i)); + } + result.add(row); + } + return result; + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + /** + * Execute the query and pass the {@link ResultSet} to the given handler. + * + * @param handler The {@link ResultSetHandler} to execute. + * @param The return type of this method. + * @return The results of {@link ResultSetHandler#apply(ResultSet)}. + */ + public V executeAndFetch(ResultSetHandler handler) { + try (ResultSet rs = executeQuery()) { + return handler.apply(rs); + } catch (SQLException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + @Override + public void close() { + try { + if (null != statement && !statement.isClosed()) { + statement.close(); + } + } catch (SQLException ex) { + logger.warn("Error closing prepared statement: {}", ex.getMessage()); + } + } + + protected final Query addParameterInternal(InternalParameterSetter setter) { + int index = getAndIncrementIndex(); + try { + setter.apply(this.statement, index); + return this; + } catch (SQLException ex) { + throw new NonTransientException("Could not apply bind parameter at index " + index, ex); + } + } + + protected V getScalarFromResultSet(ResultSet rs, Class returnType) throws SQLException { + Object value = null; + + if (Integer.class == returnType) { + value = rs.getInt(1); + } else if (Long.class == returnType) { + value = rs.getLong(1); + } else if (String.class == returnType) { + value = rs.getString(1); + } else if (Boolean.class == returnType) { + value = rs.getBoolean(1); + } else if (Double.class == returnType) { + value = rs.getDouble(1); + } else if (Date.class == returnType) { + value = rs.getDate(1); + } else if (Timestamp.class == returnType) { + value = rs.getTimestamp(1); + } else { + value = rs.getObject(1); + } + + if (null == value) { + throw new NullPointerException( + "Cannot get value from ResultSet of type " + returnType.getName()); + } + + return returnType.cast(value); + } + + protected V convert(Object value, Class returnType) { + if (Boolean.class == returnType) { + return returnType.cast(convertBoolean(value)); + } else if (Integer.class == returnType) { + return returnType.cast(convertInt(value)); + } else if (Long.class == returnType) { + return returnType.cast(convertLong(value)); + } else if (Double.class == returnType) { + return returnType.cast(convertDouble(value)); + } else if (String.class == returnType) { + return returnType.cast(convertString(value)); + } else if (value instanceof String) { + return fromJson((String) value, returnType); + } + + final String vName = value.getClass().getName(); + final String rName = returnType.getName(); + throw new NonTransientException("Cannot convert type " + vName + " to " + rName); + } + + protected Integer convertInt(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Integer) { + return (Integer) value; + } + + if (value instanceof Number) { + return ((Number) value).intValue(); + } + + return NumberUtils.toInt(value.toString()); + } + + protected Double convertDouble(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Double) { + return (Double) value; + } + + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + + return NumberUtils.toDouble(value.toString()); + } + + protected Long convertLong(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Long) { + return (Long) value; + } + + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return NumberUtils.toLong(value.toString()); + } + + protected String convertString(Object value) { + if (null == value) { + return null; + } + + if (value instanceof String) { + return (String) value; + } + + return value.toString().trim(); + } + + protected Boolean convertBoolean(Object value) { + if (null == value) { + return null; + } + + if (value instanceof Boolean) { + return (Boolean) value; + } + + if (value instanceof Number) { + return ((Number) value).intValue() != 0; + } + + String text = value.toString().trim(); + return "Y".equalsIgnoreCase(text) + || "YES".equalsIgnoreCase(text) + || "TRUE".equalsIgnoreCase(text) + || "T".equalsIgnoreCase(text) + || "1".equalsIgnoreCase(text); + } + + protected String toJson(Object value) { + if (null == value) { + return null; + } + + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException ex) { + throw new NonTransientException(ex.getMessage(), ex); + } + } + + protected V fromJson(String value, Class returnType) { + if (null == value) { + return null; + } + + try { + return objectMapper.readValue(value, returnType); + } catch (IOException ex) { + throw new NonTransientException( + "Could not convert JSON '" + value + "' to " + returnType.getName(), ex); + } + } + + protected final int getIndex() { + return index.get(); + } + + protected final int getAndIncrementIndex() { + return index.getAndIncrement(); + } + + @FunctionalInterface + private interface InternalParameterSetter { + + void apply(PreparedStatement ps, int idx) throws SQLException; + } +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java new file mode 100644 index 000000000..9d20097ca --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java @@ -0,0 +1,25 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.SQLException; + +/** + * Functional interface for {@link Query} executions that return results. + * + * @author mustafa + */ +@FunctionalInterface +public interface QueryFunction { + + R apply(Query query) throws SQLException; +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java new file mode 100644 index 000000000..ec0b64827 --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java @@ -0,0 +1,26 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Functional interface for {@link Query#executeAndFetch(ResultSetHandler)}. + * + * @author mustafa + */ +@FunctionalInterface +public interface ResultSetHandler { + + R apply(ResultSet resultSet) throws SQLException; +} diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java new file mode 100644 index 000000000..06f28889f --- /dev/null +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java @@ -0,0 +1,26 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Functional interface for operations within a transactional context. + * + * @author mustafa + */ +@FunctionalInterface +public interface TransactionalFunction { + + R apply(Connection tx) throws SQLException; +} diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V1__initial_schema.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V1__initial_schema.sql new file mode 100644 index 000000000..a76611b27 --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V1__initial_schema.sql @@ -0,0 +1,173 @@ + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR METADATA DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE meta_event_handler ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + event varchar(255) NOT NULL, + active boolean NOT NULL, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX event_handler_name_index ON meta_event_handler (name); +CREATE INDEX event_handler_event_index ON meta_event_handler (event); + +CREATE TABLE meta_task_def ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_task_def_name ON meta_task_def (name); + +CREATE TABLE meta_workflow_def ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + name varchar(255) NOT NULL, + version int NOT NULL, + latest_version int NOT NULL DEFAULT 0, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_name_version ON meta_workflow_def (name,version); +CREATE INDEX workflow_def_name_index ON meta_workflow_def (name); + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR EXECUTION DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE event_execution ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + event_handler_name varchar(255) NOT NULL, + event_name varchar(255) NOT NULL, + message_id varchar(255) NOT NULL, + execution_id varchar(255) NOT NULL, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_event_execution ON event_execution (event_handler_name,event_name,message_id); + +CREATE TABLE poll_data ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + domain varchar(255) NOT NULL, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_poll_data ON poll_data (queue_name,domain); +CREATE INDEX ON poll_data (queue_name); + +CREATE TABLE task_scheduled ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + task_key varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_workflow_id_task_key ON task_scheduled (workflow_id,task_key); + +CREATE TABLE task_in_progress ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + task_def_name varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + workflow_id varchar(255) NOT NULL, + in_progress_status boolean NOT NULL DEFAULT false, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_task_def_task_id1 ON task_in_progress (task_def_name,task_id); + +CREATE TABLE task ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + task_id varchar(255) NOT NULL, + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_task_id ON task (task_id); + +CREATE TABLE workflow ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + correlation_id varchar(255), + json_data TEXT NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_workflow_id ON workflow (workflow_id); + +CREATE TABLE workflow_def_to_workflow ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_def varchar(255) NOT NULL, + date_str varchar(60), + workflow_id varchar(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_workflow_def_date_str ON workflow_def_to_workflow (workflow_def,date_str,workflow_id); + +CREATE TABLE workflow_pending ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_type varchar(255) NOT NULL, + workflow_id varchar(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_workflow_type_workflow_id ON workflow_pending (workflow_type,workflow_id); +CREATE INDEX workflow_type_index ON workflow_pending (workflow_type); + +CREATE TABLE workflow_to_task ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + workflow_id varchar(255) NOT NULL, + task_id varchar(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_workflow_to_task_id ON workflow_to_task (workflow_id,task_id); +CREATE INDEX workflow_id_index ON workflow_to_task (workflow_id); + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR QUEUE DAO +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE queue ( + id SERIAL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_queue_name ON queue (queue_name); + +CREATE TABLE queue_message ( + id SERIAL, + created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deliver_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + queue_name varchar(255) NOT NULL, + message_id varchar(255) NOT NULL, + priority integer DEFAULT 0, + popped boolean DEFAULT false, + offset_time_seconds BIGINT, + payload TEXT, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX unique_queue_name_message_id ON queue_message (queue_name,message_id); +CREATE INDEX combo_queue_message ON queue_message (queue_name,popped,deliver_on,created_on); diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V2__1009_Fix_PostgresExecutionDAO_Index.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V2__1009_Fix_PostgresExecutionDAO_Index.sql new file mode 100644 index 000000000..03b132ab0 --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V2__1009_Fix_PostgresExecutionDAO_Index.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS unique_event_execution; + +CREATE UNIQUE INDEX unique_event_execution ON event_execution (event_handler_name,event_name,execution_id); \ No newline at end of file diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V3__correlation_id_index.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V3__correlation_id_index.sql new file mode 100644 index 000000000..9ced890da --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V3__correlation_id_index.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS workflow_corr_id_index; + +CREATE INDEX workflow_corr_id_index ON workflow (correlation_id); \ No newline at end of file diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V4__new_qm_index_with_priority.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V4__new_qm_index_with_priority.sql new file mode 100644 index 000000000..23d12a37c --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V4__new_qm_index_with_priority.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS combo_queue_message; + +CREATE INDEX combo_queue_message ON queue_message (queue_name,priority,popped,deliver_on,created_on); \ No newline at end of file diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V5__new_queue_message_pk.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V5__new_queue_message_pk.sql new file mode 100644 index 000000000..6fefa6019 --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V5__new_queue_message_pk.sql @@ -0,0 +1,11 @@ +-- no longer need separate index if pk is queue_name, message_id +DROP INDEX IF EXISTS unique_queue_name_message_id; + +-- remove id primary key +ALTER TABLE queue_message DROP CONSTRAINT IF EXISTS queue_message_pkey; + +-- remove id column +ALTER TABLE queue_message DROP COLUMN IF EXISTS id; + +-- set primary key to queue_name, message_id +ALTER TABLE queue_message ADD PRIMARY KEY (queue_name, message_id); diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V6__update_pk.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V6__update_pk.sql new file mode 100644 index 000000000..24613543b --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V6__update_pk.sql @@ -0,0 +1,77 @@ +-- 1) queue_message +DROP INDEX IF EXISTS unique_queue_name_message_id; +ALTER TABLE queue_message DROP CONSTRAINT IF EXISTS queue_message_pkey; +ALTER TABLE queue_message DROP COLUMN IF EXISTS id; +ALTER TABLE queue_message ADD PRIMARY KEY (queue_name, message_id); + +-- 2) queue +DROP INDEX IF EXISTS unique_queue_name; +ALTER TABLE queue DROP CONSTRAINT IF EXISTS queue_pkey; +ALTER TABLE queue DROP COLUMN IF EXISTS id; +ALTER TABLE queue ADD PRIMARY KEY (queue_name); + +-- 3) workflow_to_task +DROP INDEX IF EXISTS unique_workflow_to_task_id; +ALTER TABLE workflow_to_task DROP CONSTRAINT IF EXISTS workflow_to_task_pkey; +ALTER TABLE workflow_to_task DROP COLUMN IF EXISTS id; +ALTER TABLE workflow_to_task ADD PRIMARY KEY (workflow_id, task_id); + +-- 4) workflow_pending +DROP INDEX IF EXISTS unique_workflow_type_workflow_id; +ALTER TABLE workflow_pending DROP CONSTRAINT IF EXISTS workflow_pending_pkey; +ALTER TABLE workflow_pending DROP COLUMN IF EXISTS id; +ALTER TABLE workflow_pending ADD PRIMARY KEY (workflow_type, workflow_id); + +-- 5) workflow_def_to_workflow +DROP INDEX IF EXISTS unique_workflow_def_date_str; +ALTER TABLE workflow_def_to_workflow DROP CONSTRAINT IF EXISTS workflow_def_to_workflow_pkey; +ALTER TABLE workflow_def_to_workflow DROP COLUMN IF EXISTS id; +ALTER TABLE workflow_def_to_workflow ADD PRIMARY KEY (workflow_def, date_str, workflow_id); + +-- 6) workflow +DROP INDEX IF EXISTS unique_workflow_id; +ALTER TABLE workflow DROP CONSTRAINT IF EXISTS workflow_pkey; +ALTER TABLE workflow DROP COLUMN IF EXISTS id; +ALTER TABLE workflow ADD PRIMARY KEY (workflow_id); + +-- 7) task +DROP INDEX IF EXISTS unique_task_id; +ALTER TABLE task DROP CONSTRAINT IF EXISTS task_pkey; +ALTER TABLE task DROP COLUMN IF EXISTS id; +ALTER TABLE task ADD PRIMARY KEY (task_id); + +-- 8) task_in_progress +DROP INDEX IF EXISTS unique_task_def_task_id1; +ALTER TABLE task_in_progress DROP CONSTRAINT IF EXISTS task_in_progress_pkey; +ALTER TABLE task_in_progress DROP COLUMN IF EXISTS id; +ALTER TABLE task_in_progress ADD PRIMARY KEY (task_def_name, task_id); + +-- 9) task_scheduled +DROP INDEX IF EXISTS unique_workflow_id_task_key; +ALTER TABLE task_scheduled DROP CONSTRAINT IF EXISTS task_scheduled_pkey; +ALTER TABLE task_scheduled DROP COLUMN IF EXISTS id; +ALTER TABLE task_scheduled ADD PRIMARY KEY (workflow_id, task_key); + +-- 10) poll_data +DROP INDEX IF EXISTS unique_poll_data; +ALTER TABLE poll_data DROP CONSTRAINT IF EXISTS poll_data_pkey; +ALTER TABLE poll_data DROP COLUMN IF EXISTS id; +ALTER TABLE poll_data ADD PRIMARY KEY (queue_name, domain); + +-- 11) event_execution +DROP INDEX IF EXISTS unique_event_execution; +ALTER TABLE event_execution DROP CONSTRAINT IF EXISTS event_execution_pkey; +ALTER TABLE event_execution DROP COLUMN IF EXISTS id; +ALTER TABLE event_execution ADD PRIMARY KEY (event_handler_name, event_name, execution_id); + +-- 12) meta_workflow_def +DROP INDEX IF EXISTS unique_name_version; +ALTER TABLE meta_workflow_def DROP CONSTRAINT IF EXISTS meta_workflow_def_pkey; +ALTER TABLE meta_workflow_def DROP COLUMN IF EXISTS id; +ALTER TABLE meta_workflow_def ADD PRIMARY KEY (name, version); + +-- 13) meta_task_def +DROP INDEX IF EXISTS unique_task_def_name; +ALTER TABLE meta_task_def DROP CONSTRAINT IF EXISTS meta_task_def_pkey; +ALTER TABLE meta_task_def DROP COLUMN IF EXISTS id; +ALTER TABLE meta_task_def ADD PRIMARY KEY (name); diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V7__new_qm_index_desc_priority.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V7__new_qm_index_desc_priority.sql new file mode 100644 index 000000000..149dcc4c5 --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V7__new_qm_index_desc_priority.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS combo_queue_message; + +CREATE INDEX combo_queue_message ON queue_message USING btree (queue_name , priority desc, popped, deliver_on, created_on) \ No newline at end of file diff --git a/postgres-persistence/src/main/resources/db/migration_postgres/V8__indexing.sql b/postgres-persistence/src/main/resources/db/migration_postgres/V8__indexing.sql new file mode 100644 index 000000000..cb924fefa --- /dev/null +++ b/postgres-persistence/src/main/resources/db/migration_postgres/V8__indexing.sql @@ -0,0 +1,47 @@ +CREATE TABLE workflow_index ( + workflow_id VARCHAR(255) NOT NULL, + correlation_id VARCHAR(128) NULL, + workflow_type VARCHAR(128) NOT NULL, + start_time TIMESTAMP WITH TIME ZONE NOT NULL, + status VARCHAR(32) NOT NULL, + json_data JSONB NOT NULL, + PRIMARY KEY (workflow_id) +); + +CREATE INDEX workflow_index_correlation_id_idx ON workflow_index (correlation_id); +CREATE INDEX workflow_index_workflow_type_idx ON workflow_index (workflow_type); +CREATE INDEX workflow_index_start_time_idx ON workflow_index (start_time); +CREATE INDEX workflow_index_status_idx ON workflow_index (status); +CREATE INDEX workflow_index_json_data_json_idx ON workflow_index USING gin(jsonb_to_tsvector('english', json_data, '["all"]')); +CREATE INDEX workflow_index_json_data_text_idx ON workflow_index USING gin(to_tsvector('english', json_data::text)); + +CREATE TABLE task_index ( + task_id VARCHAR(255) NOT NULL, + task_type VARCHAR(32) NOT NULL, + task_def_name VARCHAR(255) NOT NULL, + status VARCHAR(32) NOT NULL, + start_time TIMESTAMP WITH TIME ZONE NOT NULL, + update_time TIMESTAMP WITH TIME ZONE NOT NULL, + workflow_type VARCHAR(128) NOT NULL, + json_data JSONB NOT NULL, + PRIMARY KEY (task_id) +); + +CREATE INDEX task_index_task_id_idx ON task_index (task_id); +CREATE INDEX task_index_task_type_idx ON task_index (task_type); +CREATE INDEX task_index_task_def_name_idx ON task_index (task_def_name); +CREATE INDEX task_index_status_idx ON task_index (status); +CREATE INDEX task_index_update_time_idx ON task_index (update_time); +CREATE INDEX task_index_workflow_type_idx ON task_index (workflow_type); +CREATE INDEX task_index_json_data_json_idx ON workflow_index USING gin(jsonb_to_tsvector('english', json_data, '["all"]')); +CREATE INDEX task_index_json_data_text_idx ON workflow_index USING gin(to_tsvector('english', json_data::text)); + +CREATE TABLE task_execution_logs ( + log_id SERIAL, + task_id varchar(255) NOT NULL, + log TEXT NOT NULL, + created_time TIMESTAMP WITH TIME ZONE NOT NULL, + PRIMARY KEY (log_id) +); + +CREATE INDEX task_execution_logs_task_id_idx ON task_execution_logs (task_id); diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java new file mode 100644 index 000000000..795f8d04d --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java @@ -0,0 +1,113 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.util.List; + +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.dao.ExecutionDAO; +import com.netflix.conductor.dao.ExecutionDAOTest; +import com.netflix.conductor.model.WorkflowModel; +import com.netflix.conductor.postgres.config.PostgresConfiguration; + +import com.google.common.collect.Iterables; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + PostgresConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class PostgresExecutionDAOTest extends ExecutionDAOTest { + + @Autowired private PostgresExecutionDAO executionDAO; + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.migrate(); + } + + @Test + public void testPendingByCorrelationId() { + + WorkflowDef def = new WorkflowDef(); + def.setName("pending_count_correlation_jtest"); + + WorkflowModel workflow = createTestWorkflow(); + workflow.setWorkflowDefinition(def); + + generateWorkflows(workflow, 10); + + List bycorrelationId = + getExecutionDAO() + .getWorkflowsByCorrelationId( + "pending_count_correlation_jtest", "corr001", true); + assertNotNull(bycorrelationId); + assertEquals(10, bycorrelationId.size()); + } + + @Test + public void testRemoveWorkflow() { + WorkflowDef def = new WorkflowDef(); + def.setName("workflow"); + + WorkflowModel workflow = createTestWorkflow(); + workflow.setWorkflowDefinition(def); + + List ids = generateWorkflows(workflow, 1); + + assertEquals(1, getExecutionDAO().getPendingWorkflowCount("workflow")); + ids.forEach(wfId -> getExecutionDAO().removeWorkflow(wfId)); + assertEquals(0, getExecutionDAO().getPendingWorkflowCount("workflow")); + } + + @Test + public void testRemoveWorkflowWithExpiry() { + WorkflowDef def = new WorkflowDef(); + def.setName("workflow"); + + WorkflowModel workflow = createTestWorkflow(); + workflow.setWorkflowDefinition(def); + + List ids = generateWorkflows(workflow, 1); + + final ExecutionDAO execDao = Mockito.spy(getExecutionDAO()); + assertEquals(1, execDao.getPendingWorkflowCount("workflow")); + ids.forEach(wfId -> execDao.removeWorkflowWithExpiry(wfId, 1)); + Mockito.verify(execDao, Mockito.timeout(10 * 1000)).removeWorkflow(Iterables.getLast(ids)); + } + + @Override + public ExecutionDAO getExecutionDAO() { + return executionDAO; + } +} diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java new file mode 100644 index 000000000..0bb1d3235 --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java @@ -0,0 +1,405 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.*; + +import javax.sql.DataSource; + +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.postgres.config.PostgresConfiguration; +import com.netflix.conductor.postgres.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.*; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + PostgresConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "conductor.app.asyncIndexingEnabled=false", + "conductor.elasticsearch.version=0", + "conductor.indexing.type=postgres", + "spring.flyway.clean-disabled=false" + }) +@SpringBootTest +public class PostgresIndexDAOTest { + + @Autowired private PostgresIndexDAO indexDAO; + + @Autowired private ObjectMapper objectMapper; + + @Qualifier("dataSource") + @Autowired + private DataSource dataSource; + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.migrate(); + } + + private WorkflowSummary getMockWorkflowSummary(String id) { + WorkflowSummary wfs = new WorkflowSummary(); + wfs.setWorkflowId(id); + wfs.setCorrelationId("correlation-id"); + wfs.setWorkflowType("workflow-type"); + wfs.setStartTime("2023-02-07T08:42:45Z"); + wfs.setStatus(Workflow.WorkflowStatus.COMPLETED); + return wfs; + } + + private TaskSummary getMockTaskSummary(String taskId) { + TaskSummary ts = new TaskSummary(); + ts.setTaskId(taskId); + ts.setTaskType("task-type"); + ts.setTaskDefName("task-def-name"); + ts.setStatus(Task.Status.COMPLETED); + ts.setStartTime("2023-02-07T09:41:45Z"); + ts.setUpdateTime("2023-02-07T09:42:45Z"); + ts.setWorkflowType("workflow-type"); + return ts; + } + + private TaskExecLog getMockTaskExecutionLog(String taskId, long createdTime, String log) { + TaskExecLog tse = new TaskExecLog(); + tse.setTaskId(taskId); + tse.setLog(log); + tse.setCreatedTime(createdTime); + return tse; + } + + private void compareWorkflowSummary(WorkflowSummary wfs) throws SQLException { + List> result = + queryDb( + String.format( + "SELECT * FROM workflow_index WHERE workflow_id = '%s'", + wfs.getWorkflowId())); + assertEquals("Wrong number of rows returned", 1, result.size()); + assertEquals( + "Workflow id does not match", + wfs.getWorkflowId(), + result.get(0).get("workflow_id")); + assertEquals( + "Correlation id does not match", + wfs.getCorrelationId(), + result.get(0).get("correlation_id")); + assertEquals( + "Workflow type does not match", + wfs.getWorkflowType(), + result.get(0).get("workflow_type")); + TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(wfs.getStartTime()); + Timestamp startTime = Timestamp.from(Instant.from(ta)); + assertEquals("Start time does not match", startTime, result.get(0).get("start_time")); + assertEquals( + "Status does not match", wfs.getStatus().toString(), result.get(0).get("status")); + } + + private List> queryDb(String query) throws SQLException { + try (Connection c = dataSource.getConnection()) { + try (Query q = new Query(objectMapper, c, query)) { + return q.executeAndFetchMap(); + } + } + } + + private void compareTaskSummary(TaskSummary ts) throws SQLException { + List> result = + queryDb( + String.format( + "SELECT * FROM task_index WHERE task_id = '%s'", ts.getTaskId())); + assertEquals("Wrong number of rows returned", 1, result.size()); + assertEquals("Task id does not match", ts.getTaskId(), result.get(0).get("task_id")); + assertEquals("Task type does not match", ts.getTaskType(), result.get(0).get("task_type")); + assertEquals( + "Task def name does not match", + ts.getTaskDefName(), + result.get(0).get("task_def_name")); + TemporalAccessor startTa = DateTimeFormatter.ISO_INSTANT.parse(ts.getStartTime()); + Timestamp startTime = Timestamp.from(Instant.from(startTa)); + assertEquals("Start time does not match", startTime, result.get(0).get("start_time")); + TemporalAccessor updateTa = DateTimeFormatter.ISO_INSTANT.parse(ts.getUpdateTime()); + Timestamp updateTime = Timestamp.from(Instant.from(updateTa)); + assertEquals("Update time does not match", updateTime, result.get(0).get("update_time")); + assertEquals( + "Status does not match", ts.getStatus().toString(), result.get(0).get("status")); + assertEquals( + "Workflow type does not match", + ts.getWorkflowType().toString(), + result.get(0).get("workflow_type")); + } + + @Test + public void testIndexNewWorkflow() throws SQLException { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id"); + + indexDAO.indexWorkflow(wfs); + + compareWorkflowSummary(wfs); + } + + @Test + public void testIndexExistingWorkflow() throws SQLException { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id"); + + indexDAO.indexWorkflow(wfs); + + compareWorkflowSummary(wfs); + + wfs.setStatus(Workflow.WorkflowStatus.FAILED); + + indexDAO.indexWorkflow(wfs); + + compareWorkflowSummary(wfs); + } + + @Test + public void testIndexNewTask() throws SQLException { + TaskSummary ts = getMockTaskSummary("task-id"); + + indexDAO.indexTask(ts); + + compareTaskSummary(ts); + } + + @Test + public void testIndexExistingTask() throws SQLException { + TaskSummary ts = getMockTaskSummary("task-id"); + + indexDAO.indexTask(ts); + + compareTaskSummary(ts); + + ts.setStatus(Task.Status.FAILED); + + indexDAO.indexTask(ts); + + compareTaskSummary(ts); + } + + @Test + public void testAddTaskExecutionLogs() throws SQLException { + List logs = new ArrayList<>(); + String taskId = UUID.randomUUID().toString(); + logs.add(getMockTaskExecutionLog(taskId, 1675845986000L, "Log 1")); + logs.add(getMockTaskExecutionLog(taskId, 1675845987000L, "Log 2")); + + indexDAO.addTaskExecutionLogs(logs); + + List> records = + queryDb("SELECT * FROM task_execution_logs ORDER BY created_time ASC"); + assertEquals("Wrong number of logs returned", 2, records.size()); + assertEquals(logs.get(0).getLog(), records.get(0).get("log")); + assertEquals(new Date(1675845986000L), records.get(0).get("created_time")); + assertEquals(logs.get(1).getLog(), records.get(1).get("log")); + assertEquals(new Date(1675845987000L), records.get(1).get("created_time")); + } + + @Test + public void testSearchWorkflowSummary() { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id"); + + indexDAO.indexWorkflow(wfs); + + String query = String.format("workflowId=\"%s\"", wfs.getWorkflowId()); + SearchResult results = + indexDAO.searchWorkflowSummary(query, "*", 0, 15, new ArrayList()); + assertEquals("No results returned", 1, results.getResults().size()); + assertEquals( + "Wrong workflow returned", + wfs.getWorkflowId(), + results.getResults().get(0).getWorkflowId()); + } + + @Test + public void testFullTextSearchWorkflowSummary() { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id"); + + indexDAO.indexWorkflow(wfs); + + String freeText = "notworkflow-id"; + SearchResult results = + indexDAO.searchWorkflowSummary("", freeText, 0, 15, new ArrayList()); + assertEquals("Wrong number of results returned", 0, results.getResults().size()); + + freeText = "workflow-id"; + results = indexDAO.searchWorkflowSummary("", freeText, 0, 15, new ArrayList()); + assertEquals("No results returned", 1, results.getResults().size()); + assertEquals( + "Wrong workflow returned", + wfs.getWorkflowId(), + results.getResults().get(0).getWorkflowId()); + } + + @Test + public void testJsonSearchWorkflowSummary() { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id"); + wfs.setVersion(3); + + indexDAO.indexWorkflow(wfs); + + String freeText = "{\"correlationId\":\"not-the-id\"}"; + SearchResult results = + indexDAO.searchWorkflowSummary("", freeText, 0, 15, new ArrayList()); + assertEquals("Wrong number of results returned", 0, results.getResults().size()); + + freeText = "{\"correlationId\":\"correlation-id\", \"version\":3}"; + results = indexDAO.searchWorkflowSummary("", freeText, 0, 15, new ArrayList()); + assertEquals("No results returned", 1, results.getResults().size()); + assertEquals( + "Wrong workflow returned", + wfs.getWorkflowId(), + results.getResults().get(0).getWorkflowId()); + } + + @Test + public void testSearchWorkflowSummaryPagination() { + for (int i = 0; i < 5; i++) { + WorkflowSummary wfs = getMockWorkflowSummary("workflow-id-" + i); + indexDAO.indexWorkflow(wfs); + } + + List orderBy = Arrays.asList(new String[] {"workflowId:DESC"}); + SearchResult results = + indexDAO.searchWorkflowSummary("", "*", 0, 2, orderBy); + assertEquals("Wrong totalHits returned", 3, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "workflow-id-4", + results.getResults().get(0).getWorkflowId()); + assertEquals( + "Results returned in wrong order", + "workflow-id-3", + results.getResults().get(1).getWorkflowId()); + results = indexDAO.searchWorkflowSummary("", "*", 2, 2, orderBy); + assertEquals("Wrong totalHits returned", 5, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "workflow-id-2", + results.getResults().get(0).getWorkflowId()); + assertEquals( + "Results returned in wrong order", + "workflow-id-1", + results.getResults().get(1).getWorkflowId()); + results = indexDAO.searchWorkflowSummary("", "*", 4, 2, orderBy); + assertEquals("Wrong totalHits returned", 7, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "workflow-id-0", + results.getResults().get(0).getWorkflowId()); + } + + @Test + public void testSearchTaskSummary() { + TaskSummary ts = getMockTaskSummary("task-id"); + + indexDAO.indexTask(ts); + + String query = String.format("taskId=\"%s\"", ts.getTaskId()); + SearchResult results = + indexDAO.searchTaskSummary(query, "*", 0, 15, new ArrayList()); + assertEquals("No results returned", 1, results.getResults().size()); + assertEquals( + "Wrong task returned", ts.getTaskId(), results.getResults().get(0).getTaskId()); + } + + @Test + public void testSearchTaskSummaryPagination() { + for (int i = 0; i < 5; i++) { + TaskSummary ts = getMockTaskSummary("task-id-" + i); + indexDAO.indexTask(ts); + } + + List orderBy = Arrays.asList(new String[] {"taskId:DESC"}); + SearchResult results = indexDAO.searchTaskSummary("", "*", 0, 2, orderBy); + assertEquals("Wrong totalHits returned", 3, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "task-id-4", + results.getResults().get(0).getTaskId()); + assertEquals( + "Results returned in wrong order", + "task-id-3", + results.getResults().get(1).getTaskId()); + results = indexDAO.searchTaskSummary("", "*", 2, 2, orderBy); + assertEquals("Wrong totalHits returned", 5, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "task-id-2", + results.getResults().get(0).getTaskId()); + assertEquals( + "Results returned in wrong order", + "task-id-1", + results.getResults().get(1).getTaskId()); + results = indexDAO.searchTaskSummary("", "*", 4, 2, orderBy); + assertEquals("Wrong totalHits returned", 7, results.getTotalHits()); + assertEquals("Wrong number of results returned", 2, results.getResults().size()); + assertEquals( + "Results returned in wrong order", + "task-id-0", + results.getResults().get(0).getTaskId()); + } + + @Test + public void testGetTaskExecutionLogs() throws SQLException { + List logs = new ArrayList<>(); + String taskId = UUID.randomUUID().toString(); + logs.add(getMockTaskExecutionLog(taskId, new Date(1675845986000L).getTime(), "Log 1")); + logs.add(getMockTaskExecutionLog(taskId, new Date(1675845987000L).getTime(), "Log 2")); + + indexDAO.addTaskExecutionLogs(logs); + + List records = indexDAO.getTaskExecutionLogs(logs.get(0).getTaskId()); + assertEquals("Wrong number of logs returned", 2, records.size()); + assertEquals(logs.get(0).getLog(), records.get(0).getLog()); + assertEquals(logs.get(0).getCreatedTime(), 1675845986000L); + assertEquals(logs.get(1).getLog(), records.get(1).getLog()); + assertEquals(logs.get(1).getCreatedTime(), 1675845987000L); + } +} diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java new file mode 100644 index 000000000..a302b43c3 --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java @@ -0,0 +1,323 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.postgres.config.PostgresConfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + PostgresConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class PostgresMetadataDAOTest { + + @Autowired private PostgresMetadataDAO metadataDAO; + + @Rule public TestName name = new TestName(); + + @Autowired Flyway flyway; + + // clean the database between tests. + @Before + public void before() { + flyway.migrate(); + } + + @Test + public void testDuplicateWorkflowDef() { + WorkflowDef def = new WorkflowDef(); + def.setName("testDuplicate"); + def.setVersion(1); + + metadataDAO.createWorkflowDef(def); + + NonTransientException applicationException = + assertThrows(NonTransientException.class, () -> metadataDAO.createWorkflowDef(def)); + assertEquals( + "Workflow with testDuplicate.1 already exists!", applicationException.getMessage()); + } + + @Test + public void testRemoveNotExistingWorkflowDef() { + NonTransientException applicationException = + assertThrows( + NonTransientException.class, + () -> metadataDAO.removeWorkflowDef("test", 1)); + assertEquals( + "No such workflow definition: test version: 1", applicationException.getMessage()); + } + + @Test + public void testWorkflowDefOperations() { + WorkflowDef def = new WorkflowDef(); + def.setName("test"); + def.setVersion(1); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setOwnerApp("ownerApp"); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + + metadataDAO.createWorkflowDef(def); + + List all = metadataDAO.getAllWorkflowDefs(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(1, all.get(0).getVersion()); + + WorkflowDef found = metadataDAO.getWorkflowDef("test", 1).get(); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + + def.setVersion(3); + metadataDAO.createWorkflowDef(def); + + all = metadataDAO.getAllWorkflowDefs(); + assertNotNull(all); + assertEquals(2, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(1, all.get(0).getVersion()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(def.getVersion(), found.getVersion()); + assertEquals(3, found.getVersion()); + + all = metadataDAO.getAllLatest(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals(3, all.get(0).getVersion()); + + all = metadataDAO.getAllVersions(def.getName()); + assertNotNull(all); + assertEquals(2, all.size()); + assertEquals("test", all.get(0).getName()); + assertEquals("test", all.get(1).getName()); + assertEquals(1, all.get(0).getVersion()); + assertEquals(3, all.get(1).getVersion()); + + def.setDescription("updated"); + metadataDAO.updateWorkflowDef(def); + found = metadataDAO.getWorkflowDef(def.getName(), def.getVersion()).get(); + assertEquals(def.getDescription(), found.getDescription()); + + List allnames = metadataDAO.findAll(); + assertNotNull(allnames); + assertEquals(1, allnames.size()); + assertEquals(def.getName(), allnames.get(0)); + + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(3, found.getVersion()); + + metadataDAO.removeWorkflowDef("test", 3); + Optional deleted = metadataDAO.getWorkflowDef("test", 3); + assertFalse(deleted.isPresent()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(2, found.getVersion()); + + metadataDAO.removeWorkflowDef("test", 1); + deleted = metadataDAO.getWorkflowDef("test", 1); + assertFalse(deleted.isPresent()); + + found = metadataDAO.getLatestWorkflowDef(def.getName()).get(); + assertEquals(def.getName(), found.getName()); + assertEquals(2, found.getVersion()); + } + + @Test + public void testTaskDefOperations() { + TaskDef def = new TaskDef("taskA"); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setInputKeys(Arrays.asList("a", "b", "c")); + def.setOutputKeys(Arrays.asList("01", "o2")); + def.setOwnerApp("ownerApp"); + def.setRetryCount(3); + def.setRetryDelaySeconds(100); + def.setRetryLogic(TaskDef.RetryLogic.FIXED); + def.setTimeoutPolicy(TaskDef.TimeoutPolicy.ALERT_ONLY); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + + metadataDAO.createTaskDef(def); + + TaskDef found = metadataDAO.getTaskDef(def.getName()); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + + def.setDescription("updated description"); + metadataDAO.updateTaskDef(def); + found = metadataDAO.getTaskDef(def.getName()); + assertTrue(EqualsBuilder.reflectionEquals(def, found)); + assertEquals("updated description", found.getDescription()); + + for (int i = 0; i < 9; i++) { + TaskDef tdf = new TaskDef("taskA" + i); + metadataDAO.createTaskDef(tdf); + } + + List all = metadataDAO.getAllTaskDefs(); + assertNotNull(all); + assertEquals(10, all.size()); + Set allnames = all.stream().map(TaskDef::getName).collect(Collectors.toSet()); + assertEquals(10, allnames.size()); + List sorted = allnames.stream().sorted().collect(Collectors.toList()); + assertEquals(def.getName(), sorted.get(0)); + + for (int i = 0; i < 9; i++) { + assertEquals(def.getName() + i, sorted.get(i + 1)); + } + + for (int i = 0; i < 9; i++) { + metadataDAO.removeTaskDef(def.getName() + i); + } + all = metadataDAO.getAllTaskDefs(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals(def.getName(), all.get(0).getName()); + } + + @Test + public void testRemoveNotExistingTaskDef() { + NonTransientException applicationException = + assertThrows( + NonTransientException.class, + () -> metadataDAO.removeTaskDef("test" + UUID.randomUUID().toString())); + assertEquals("No such task definition", applicationException.getMessage()); + } + + @Test + public void testEventHandlers() { + String event1 = "SQS::arn:account090:sqstest1"; + String event2 = "SQS::arn:account090:sqstest2"; + + EventHandler eventHandler = new EventHandler(); + eventHandler.setName(UUID.randomUUID().toString()); + eventHandler.setActive(false); + EventHandler.Action action = new EventHandler.Action(); + action.setAction(EventHandler.Action.Type.start_workflow); + action.setStart_workflow(new EventHandler.StartWorkflow()); + action.getStart_workflow().setName("workflow_x"); + eventHandler.getActions().add(action); + eventHandler.setEvent(event1); + + metadataDAO.addEventHandler(eventHandler); + List all = metadataDAO.getAllEventHandlers(); + assertNotNull(all); + assertEquals(1, all.size()); + assertEquals(eventHandler.getName(), all.get(0).getName()); + assertEquals(eventHandler.getEvent(), all.get(0).getEvent()); + + List byEvents = metadataDAO.getEventHandlersForEvent(event1, true); + assertNotNull(byEvents); + assertEquals(0, byEvents.size()); // event is marked as in-active + + eventHandler.setActive(true); + eventHandler.setEvent(event2); + metadataDAO.updateEventHandler(eventHandler); + + all = metadataDAO.getAllEventHandlers(); + assertNotNull(all); + assertEquals(1, all.size()); + + byEvents = metadataDAO.getEventHandlersForEvent(event1, true); + assertNotNull(byEvents); + assertEquals(0, byEvents.size()); + + byEvents = metadataDAO.getEventHandlersForEvent(event2, true); + assertNotNull(byEvents); + assertEquals(1, byEvents.size()); + } + + @Test + public void testGetAllWorkflowDefsLatestVersions() { + WorkflowDef def = new WorkflowDef(); + def.setName("test1"); + def.setVersion(1); + def.setDescription("description"); + def.setCreatedBy("unit_test"); + def.setCreateTime(1L); + def.setOwnerApp("ownerApp"); + def.setUpdatedBy("unit_test2"); + def.setUpdateTime(2L); + metadataDAO.createWorkflowDef(def); + + def.setName("test2"); + metadataDAO.createWorkflowDef(def); + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + + def.setName("test3"); + def.setVersion(1); + metadataDAO.createWorkflowDef(def); + def.setVersion(2); + metadataDAO.createWorkflowDef(def); + def.setVersion(3); + metadataDAO.createWorkflowDef(def); + + // Placed the values in a map because they might not be stored in order of defName. + // To test, needed to confirm that the versions are correct for the definitions. + Map allMap = + metadataDAO.getAllWorkflowDefsLatestVersions().stream() + .collect(Collectors.toMap(WorkflowDef::getName, Function.identity())); + + assertNotNull(allMap); + assertEquals(4, allMap.size()); + assertEquals(1, allMap.get("test1").getVersion()); + assertEquals(2, allMap.get("test2").getVersion()); + assertEquals(3, allMap.get("test3").getVersion()); + } +} diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java new file mode 100644 index 000000000..caf291d9d --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java @@ -0,0 +1,417 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.dao; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import org.flywaydb.core.Flyway; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.postgres.config.PostgresConfiguration; +import com.netflix.conductor.postgres.util.Query; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@ContextConfiguration( + classes = { + TestObjectMapperConfiguration.class, + PostgresConfiguration.class, + FlywayAutoConfiguration.class + }) +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.flyway.clean-disabled=false") +public class PostgresQueueDAOTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(PostgresQueueDAOTest.class); + + @Autowired private PostgresQueueDAO queueDAO; + + @Qualifier("dataSource") + @Autowired + private DataSource dataSource; + + @Autowired private ObjectMapper objectMapper; + + @Rule public TestName name = new TestName(); + + @Autowired Flyway flyway; + + + // clean the database between tests. + @Before + public void before() { + try (Connection conn = dataSource.getConnection()){ + conn.setAutoCommit(true); + String[] stmts = new String[]{"truncate table queue;", "truncate table queue_message;"}; + for (String stmt : stmts) { + conn.prepareStatement(stmt).executeUpdate(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @Test + public void complexQueueTest() { + String queueName = "TestQueue"; + long offsetTimeInSecond = 0; + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.push(queueName, messageId, offsetTimeInSecond); + } + int size = queueDAO.getSize(queueName); + assertEquals(10, size); + Map details = queueDAO.queuesDetail(); + assertEquals(1, details.size()); + assertEquals(10L, details.get(queueName).longValue()); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + + List popped = queueDAO.pop(queueName, 10, 100); + assertNotNull(popped); + assertEquals(10, popped.size()); + + Map>> verbose = queueDAO.queuesDetailVerbose(); + assertEquals(1, verbose.size()); + long shardSize = verbose.get(queueName).get("a").get("size"); + long unackedSize = verbose.get(queueName).get("a").get("uacked"); + assertEquals(0, shardSize); + assertEquals(10, unackedSize); + + popped.forEach(messageId -> queueDAO.ack(queueName, messageId)); + + verbose = queueDAO.queuesDetailVerbose(); + assertEquals(1, verbose.size()); + shardSize = verbose.get(queueName).get("a").get("size"); + unackedSize = verbose.get(queueName).get("a").get("uacked"); + assertEquals(0, shardSize); + assertEquals(0, unackedSize); + + popped = queueDAO.pop(queueName, 10, 100); + assertNotNull(popped); + assertEquals(0, popped.size()); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + size = queueDAO.getSize(queueName); + assertEquals(10, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertTrue(queueDAO.containsMessage(queueName, messageId)); + queueDAO.remove(queueName, messageId); + } + + size = queueDAO.getSize(queueName); + assertEquals(0, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.pushIfNotExists(queueName, messageId, offsetTimeInSecond); + } + queueDAO.flush(queueName); + size = queueDAO.getSize(queueName); + assertEquals(0, size); + } + + /** + * Test fix for https://github.com/Netflix/conductor/issues/399 + * + * @since 1.8.2-rc5 + */ + @Test + public void pollMessagesTest() { + final List messages = new ArrayList<>(); + final String queueName = "issue399_testQueue"; + final int totalSize = 10; + + for (int i = 0; i < totalSize; i++) { + String payload = "{\"id\": " + i + ", \"msg\":\"test " + i + "\"}"; + Message m = new Message("testmsg-" + i, payload, ""); + if (i % 2 == 0) { + // Set priority on message with pair id + m.setPriority(99 - i); + } + messages.add(m); + } + + // Populate the queue with our test message batch + queueDAO.push(queueName, ImmutableList.copyOf(messages)); + + // Assert that all messages were persisted and no extras are in there + assertEquals("Queue size mismatch", totalSize, queueDAO.getSize(queueName)); + + List zeroPoll = queueDAO.pollMessages(queueName, 0, 10_000); + assertTrue("Zero poll should be empty", zeroPoll.isEmpty()); + + final int firstPollSize = 3; + List firstPoll = queueDAO.pollMessages(queueName, firstPollSize, 10_000); + assertNotNull("First poll was null", firstPoll); + assertFalse("First poll was empty", firstPoll.isEmpty()); + assertEquals("First poll size mismatch", firstPollSize, firstPoll.size()); + + final int secondPollSize = 4; + List secondPoll = queueDAO.pollMessages(queueName, secondPollSize, 10_000); + assertNotNull("Second poll was null", secondPoll); + assertFalse("Second poll was empty", secondPoll.isEmpty()); + assertEquals("Second poll size mismatch", secondPollSize, secondPoll.size()); + + // Assert that the total queue size hasn't changed + assertEquals( + "Total queue size should have remained the same", + totalSize, + queueDAO.getSize(queueName)); + + // Assert that our un-popped messages match our expected size + final long expectedSize = totalSize - firstPollSize - secondPollSize; + try (Connection c = dataSource.getConnection()) { + String UNPOPPED = + "SELECT COUNT(*) FROM queue_message WHERE queue_name = ? AND popped = false"; + try (Query q = new Query(objectMapper, c, UNPOPPED)) { + long count = q.addParameter(queueName).executeCount(); + assertEquals("Remaining queue size mismatch", expectedSize, count); + } + } catch (Exception ex) { + fail(ex.getMessage()); + } + } + + /** Test fix for https://github.com/Netflix/conductor/issues/1892 */ + @Test + public void containsMessageTest() { + String queueName = "TestQueue"; + long offsetTimeInSecond = 0; + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + queueDAO.push(queueName, messageId, offsetTimeInSecond); + } + int size = queueDAO.getSize(queueName); + assertEquals(10, size); + + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertTrue(queueDAO.containsMessage(queueName, messageId)); + queueDAO.remove(queueName, messageId); + } + for (int i = 0; i < 10; i++) { + String messageId = "msg" + i; + assertFalse(queueDAO.containsMessage(queueName, messageId)); + } + } + + /** + * Test fix for https://github.com/Netflix/conductor/issues/448 + * + * @since 1.8.2-rc5 + */ + @Test + public void pollDeferredMessagesTest() throws InterruptedException { + final List messages = new ArrayList<>(); + final String queueName = "issue448_testQueue"; + final int totalSize = 10; + + for (int i = 0; i < totalSize; i++) { + int offset = 0; + if (i < 5) { + offset = 0; + } else if (i == 6 || i == 7) { + // Purposefully skipping id:5 to test out of order deliveries + // Set id:6 and id:7 for a 2s delay to be picked up in the second polling batch + offset = 5; + } else { + // Set all other queue messages to have enough of a delay that they won't + // accidentally + // be picked up. + offset = 10_000 + i; + } + + String payload = "{\"id\": " + i + ",\"offset_time_seconds\":" + offset + "}"; + Message m = new Message("testmsg-" + i, payload, ""); + messages.add(m); + queueDAO.push(queueName, "testmsg-" + i, offset); + } + + // Assert that all messages were persisted and no extras are in there + assertEquals("Queue size mismatch", totalSize, queueDAO.getSize(queueName)); + + final int firstPollSize = 4; + List firstPoll = queueDAO.pollMessages(queueName, firstPollSize, 100); + assertNotNull("First poll was null", firstPoll); + assertFalse("First poll was empty", firstPoll.isEmpty()); + assertEquals("First poll size mismatch", firstPollSize, firstPoll.size()); + + List firstPollMessageIds = + messages.stream() + .map(Message::getId) + .collect(Collectors.toList()) + .subList(0, firstPollSize + 1); + + for (int i = 0; i < firstPollSize; i++) { + String actual = firstPoll.get(i).getId(); + assertTrue("Unexpected Id: " + actual, firstPollMessageIds.contains(actual)); + } + + final int secondPollSize = 3; + + // Sleep a bit to get the next batch of messages + LOGGER.info("Sleeping for second poll..."); + Thread.sleep(5_000); + + // Poll for many more messages than expected + List secondPoll = queueDAO.pollMessages(queueName, secondPollSize + 10, 100); + assertNotNull("Second poll was null", secondPoll); + assertFalse("Second poll was empty", secondPoll.isEmpty()); + assertEquals("Second poll size mismatch", secondPollSize, secondPoll.size()); + + List expectedIds = Arrays.asList("testmsg-4", "testmsg-6", "testmsg-7"); + for (int i = 0; i < secondPollSize; i++) { + String actual = secondPoll.get(i).getId(); + assertTrue("Unexpected Id: " + actual, expectedIds.contains(actual)); + } + + // Assert that the total queue size hasn't changed + assertEquals( + "Total queue size should have remained the same", + totalSize, + queueDAO.getSize(queueName)); + + // Assert that our un-popped messages match our expected size + final long expectedSize = totalSize - firstPollSize - secondPollSize; + try (Connection c = dataSource.getConnection()) { + String UNPOPPED = + "SELECT COUNT(*) FROM queue_message WHERE queue_name = ? AND popped = false"; + try (Query q = new Query(objectMapper, c, UNPOPPED)) { + long count = q.addParameter(queueName).executeCount(); + assertEquals("Remaining queue size mismatch", expectedSize, count); + } + } catch (Exception ex) { + fail(ex.getMessage()); + } + } + + //@Test + public void processUnacksTest() { + processUnacks( + () -> { + // Process unacks + queueDAO.processUnacks("process_unacks_test"); + }, + "process_unacks_test"); + } + + //@Test + public void processAllUnacksTest() { + processUnacks( + () -> { + // Process all unacks + queueDAO.processAllUnacks(); + }, + "process_unacks_test"); + } + + private void processUnacks(Runnable unack, String queueName) { + // Count of messages in the queue(s) + final int count = 10; + // Number of messages to process acks for + final int unackedCount = 4; + // A secondary queue to make sure we don't accidentally process other queues + final String otherQueueName = "process_unacks_test_other_queue"; + + // Create testing queue with some messages (but not all) that will be popped/acked. + for (int i = 0; i < count; i++) { + int offset = 0; + if (i >= unackedCount) { + offset = 1_000_000; + } + + queueDAO.push(queueName, "unack-" + i, offset); + } + + // Create a second queue to make sure that unacks don't occur for it + for (int i = 0; i < count; i++) { + queueDAO.push(otherQueueName, "other-" + i, 0); + } + + // Poll for first batch of messages (should be equal to unackedCount) + List polled = queueDAO.pollMessages(queueName, 100, 10_000); + assertNotNull(polled); + assertFalse(polled.isEmpty()); + assertEquals(unackedCount, polled.size()); + + // Poll messages from the other queue so we know they don't get unacked later + queueDAO.pollMessages(otherQueueName, 100, 10_000); + + // Ack one of the polled messages + assertTrue(queueDAO.ack(queueName, "unack-1")); + + // Should have one less un-acked popped message in the queue + Long uacked = queueDAO.queuesDetailVerbose().get(queueName).get("a").get("uacked"); + assertNotNull(uacked); + assertEquals(uacked.longValue(), unackedCount - 1); + + unack.run(); + + // Check uacks for both queues after processing + Map>> details = queueDAO.queuesDetailVerbose(); + uacked = details.get(queueName).get("a").get("uacked"); + assertNotNull(uacked); + assertEquals( + "The messages that were polled should be unacked still", + uacked.longValue(), + unackedCount - 1); + + Long otherUacked = details.get(otherQueueName).get("a").get("uacked"); + assertNotNull(otherUacked); + assertEquals( + "Other queue should have all unacked messages", otherUacked.longValue(), count); + + Long size = queueDAO.queuesDetail().get(queueName); + assertNotNull(size); + assertEquals(size.longValue(), count - unackedCount); + } +} diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java new file mode 100644 index 000000000..299444a95 --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java @@ -0,0 +1,453 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.performance; + +// SBMTODO: this test needs to be migrated +// reference - https://github.com/Netflix/conductor/pull/1940 +// @Ignore("This test cannot be automated") +// public class PerformanceTest { +// +// public static final int MSGS = 1000; +// public static final int PRODUCER_BATCH = 10; // make sure MSGS % PRODUCER_BATCH == 0 +// public static final int PRODUCERS = 4; +// public static final int WORKERS = 8; +// public static final int OBSERVERS = 4; +// public static final int OBSERVER_DELAY = 5000; +// public static final int UNACK_RUNNERS = 10; +// public static final int UNACK_DELAY = 500; +// public static final int WORKER_BATCH = 10; +// public static final int WORKER_BATCH_TIMEOUT = 500; +// public static final int COMPLETION_MONITOR_DELAY = 1000; +// +// private DataSource dataSource; +// private QueueDAO Q; +// private ExecutionDAO E; +// +// private final ExecutorService threadPool = Executors.newFixedThreadPool(PRODUCERS + WORKERS + +// OBSERVERS + UNACK_RUNNERS); +// private static final Logger LOGGER = LoggerFactory.getLogger(PerformanceTest.class); +// +// @Before +// public void setUp() { +// TestConfiguration testConfiguration = new TestConfiguration(); +// configuration = new TestPostgresConfiguration(testConfiguration, +// +// "jdbc:postgresql://localhost:54320/conductor?charset=utf8&parseTime=true&interpolateParams=true", +// 10, 2); +// PostgresDataSourceProvider dataSource = new PostgresDataSourceProvider(configuration); +// this.dataSource = dataSource.get(); +// resetAllData(this.dataSource); +// flywayMigrate(this.dataSource); +// +// final ObjectMapper objectMapper = new JsonMapperProvider().get(); +// Q = new PostgresQueueDAO(objectMapper, this.dataSource); +// E = new PostgresExecutionDAO(objectMapper, this.dataSource); +// } +// +// @After +// public void tearDown() throws Exception { +// resetAllData(dataSource); +// } +// +// public static final String QUEUE = "task_queue"; +// +// @Test +// public void testQueueDaoPerformance() throws InterruptedException { +// AtomicBoolean stop = new AtomicBoolean(false); +// Stopwatch start = Stopwatch.createStarted(); +// AtomicInteger poppedCoutner = new AtomicInteger(0); +// HashMultiset allPopped = HashMultiset.create(); +// +// // Consumers - workers +// for (int i = 0; i < WORKERS; i++) { +// threadPool.submit(() -> { +// while (!stop.get()) { +// List pop = Q.pollMessages(QUEUE, WORKER_BATCH, WORKER_BATCH_TIMEOUT); +// LOGGER.info("Popped {} messages", pop.size()); +// poppedCoutner.accumulateAndGet(pop.size(), Integer::sum); +// +// if (pop.size() == 0) { +// try { +// Thread.sleep(200); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } else { +// LOGGER.info("Popped {}", +// pop.stream().map(Message::getId).collect(Collectors.toList())); +// } +// +// pop.forEach(popped -> { +// synchronized (allPopped) { +// allPopped.add(popped.getId()); +// } +// boolean exists = Q.containsMessage(QUEUE, popped.getId()); +// boolean ack = Q.ack(QUEUE, popped.getId()); +// +// if (ack && exists) { +// // OK +// } else { +// LOGGER.error("Exists & Ack did not succeed for msg: {}", popped); +// } +// }); +// } +// }); +// } +// +// // Producers +// List> producers = Lists.newArrayList(); +// for (int i = 0; i < PRODUCERS; i++) { +// Future producer = threadPool.submit(() -> { +// try { +// // N messages +// for (int j = 0; j < MSGS / PRODUCER_BATCH; j++) { +// List randomMessages = getRandomMessages(PRODUCER_BATCH); +// Q.push(QUEUE, randomMessages); +// LOGGER.info("Pushed {} messages", PRODUCER_BATCH); +// LOGGER.info("Pushed {}", +// randomMessages.stream().map(Message::getId).collect(Collectors.toList())); +// } +// LOGGER.info("Pushed ALL"); +// } catch (Exception e) { +// LOGGER.error("Something went wrong with producer", e); +// throw new RuntimeException(e); +// } +// }); +// +// producers.add(producer); +// } +// +// // Observers +// for (int i = 0; i < OBSERVERS; i++) { +// threadPool.submit(() -> { +// while (!stop.get()) { +// try { +// int size = Q.getSize(QUEUE); +// Q.queuesDetail(); +// LOGGER.info("Size {} messages", size); +// } catch (Exception e) { +// LOGGER.info("Queue size failed, nevermind"); +// } +// +// try { +// Thread.sleep(OBSERVER_DELAY); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } +// }); +// } +// +// // Consumers - unack processor +// for (int i = 0; i < UNACK_RUNNERS; i++) { +// threadPool.submit(() -> { +// while (!stop.get()) { +// try { +// Q.processUnacks(QUEUE); +// } catch (Exception e) { +// LOGGER.info("Unack failed, nevermind", e); +// continue; +// } +// LOGGER.info("Unacked"); +// try { +// Thread.sleep(UNACK_DELAY); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } +// }); +// } +// +// long elapsed; +// while (true) { +// try { +// Thread.sleep(COMPLETION_MONITOR_DELAY); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// +// int size = Q.getSize(QUEUE); +// LOGGER.info("MONITOR SIZE : {}", size); +// +// if (size == 0 && producers.stream().map(Future::isDone).reduce(true, (b1, b2) -> b1 && +// b2)) { +// elapsed = start.elapsed(TimeUnit.MILLISECONDS); +// stop.set(true); +// break; +// } +// } +// +// threadPool.awaitTermination(10, TimeUnit.SECONDS); +// threadPool.shutdown(); +// LOGGER.info("Finished in {} ms", elapsed); +// LOGGER.info("Throughput {} msgs/second", ((MSGS * PRODUCERS) / (elapsed * 1.0)) * 1000); +// LOGGER.info("Threads finished"); +// if (poppedCoutner.get() != MSGS * PRODUCERS) { +// synchronized (allPopped) { +// List duplicates = allPopped.entrySet().stream() +// .filter(stringEntry -> stringEntry.getCount() > 1) +// .map(stringEntry -> stringEntry.getElement() + ": " + stringEntry.getCount()) +// .collect(Collectors.toList()); +// +// LOGGER.error("Found duplicate pops: " + duplicates); +// } +// throw new RuntimeException("Popped " + poppedCoutner.get() + " != produced: " + MSGS * +// PRODUCERS); +// } +// } +// +// @Test +// public void testExecDaoPerformance() throws InterruptedException { +// AtomicBoolean stop = new AtomicBoolean(false); +// Stopwatch start = Stopwatch.createStarted(); +// BlockingDeque msgQueue = new LinkedBlockingDeque<>(1000); +// HashMultiset allPopped = HashMultiset.create(); +// +// // Consumers - workers +// for (int i = 0; i < WORKERS; i++) { +// threadPool.submit(() -> { +// while (!stop.get()) { +// List popped = new ArrayList<>(); +// while (true) { +// try { +// Task poll; +// poll = msgQueue.poll(10, TimeUnit.MILLISECONDS); +// +// if (poll == null) { +// // poll timed out +// continue; +// } +// synchronized (allPopped) { +// allPopped.add(poll.getTaskId()); +// } +// popped.add(poll); +// if (stop.get() || popped.size() == WORKER_BATCH) { +// break; +// } +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } +// +// LOGGER.info("Popped {} messages", popped.size()); +// LOGGER.info("Popped {}", +// popped.stream().map(Task::getTaskId).collect(Collectors.toList())); +// +// // Polling +// popped.stream() +// .peek(task -> { +// task.setWorkerId("someWorker"); +// task.setPollCount(task.getPollCount() + 1); +// task.setStartTime(System.currentTimeMillis()); +// }) +// .forEach(task -> { +// try { +// // should always be false +// boolean concurrentLimit = E.exceedsInProgressLimit(task); +// task.setStartTime(System.currentTimeMillis()); +// E.updateTask(task); +// LOGGER.info("Polled {}", task.getTaskId()); +// } catch (Exception e) { +// LOGGER.error("Something went wrong with worker during poll", e); +// throw new RuntimeException(e); +// } +// }); +// +// popped.forEach(task -> { +// try { +// +// String wfId = task.getWorkflowInstanceId(); +// Workflow workflow = E.getWorkflow(wfId, true); +// E.getTask(task.getTaskId()); +// +// task.setStatus(Task.Status.COMPLETED); +// task.setWorkerId("someWorker"); +// task.setOutputData(Collections.singletonMap("a", "b")); +// E.updateTask(task); +// E.updateWorkflow(workflow); +// LOGGER.info("Updated {}", task.getTaskId()); +// } catch (Exception e) { +// LOGGER.error("Something went wrong with worker during update", e); +// throw new RuntimeException(e); +// } +// }); +// +// } +// }); +// } +// +// Multiset pushedTasks = HashMultiset.create(); +// +// // Producers +// List> producers = Lists.newArrayList(); +// for (int i = 0; i < PRODUCERS; i++) { +// Future producer = threadPool.submit(() -> { +// // N messages +// for (int j = 0; j < MSGS / PRODUCER_BATCH; j++) { +// List randomTasks = getRandomTasks(PRODUCER_BATCH); +// +// Workflow wf = getWorkflow(randomTasks); +// E.createWorkflow(wf); +// +// E.createTasks(randomTasks); +// randomTasks.forEach(t -> { +// try { +// boolean offer = false; +// while (!offer) { +// offer = msgQueue.offer(t, 10, TimeUnit.MILLISECONDS); +// } +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// }); +// LOGGER.info("Pushed {} messages", PRODUCER_BATCH); +// List collect = +// randomTasks.stream().map(Task::getTaskId).collect(Collectors.toList()); +// synchronized (pushedTasks) { +// pushedTasks.addAll(collect); +// } +// LOGGER.info("Pushed {}", collect); +// } +// LOGGER.info("Pushed ALL"); +// }); +// +// producers.add(producer); +// } +// +// // Observers +// for (int i = 0; i < OBSERVERS; i++) { +// threadPool.submit(() -> { +// while (!stop.get()) { +// try { +// List size = E.getPendingTasksForTaskType("taskType"); +// LOGGER.info("Size {} messages", size.size()); +// LOGGER.info("Size q {} messages", msgQueue.size()); +// synchronized (allPopped) { +// LOGGER.info("All pp {} messages", allPopped.size()); +// } +// LOGGER.info("Workflows by correlation id size: {}", +// E.getWorkflowsByCorrelationId("abcd", "1", true).size()); +// LOGGER.info("Workflows by correlation id size: {}", +// E.getWorkflowsByCorrelationId("abcd", "2", true).size()); +// LOGGER.info("Workflows running ids: {}", E.getRunningWorkflowIds("abcd", +// 1)); +// LOGGER.info("Workflows pending count: {}", +// E.getPendingWorkflowCount("abcd")); +// } catch (Exception e) { +// LOGGER.warn("Observer failed ", e); +// } +// try { +// Thread.sleep(OBSERVER_DELAY); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } +// }); +// } +// +// long elapsed; +// while (true) { +// try { +// Thread.sleep(COMPLETION_MONITOR_DELAY); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// +// int size; +// try { +// size = E.getPendingTasksForTaskType("taskType").size(); +// } catch (Exception e) { +// LOGGER.warn("Monitor failed", e); +// continue; +// } +// LOGGER.info("MONITOR SIZE : {}", size); +// +// if (size == 0 && producers.stream().map(Future::isDone).reduce(true, (b1, b2) -> b1 && +// b2)) { +// elapsed = start.elapsed(TimeUnit.MILLISECONDS); +// stop.set(true); +// break; +// } +// } +// +// threadPool.awaitTermination(10, TimeUnit.SECONDS); +// threadPool.shutdown(); +// LOGGER.info("Finished in {} ms", elapsed); +// LOGGER.info("Throughput {} msgs/second", ((MSGS * PRODUCERS) / (elapsed * 1.0)) * 1000); +// LOGGER.info("Threads finished"); +// +// List duplicates = pushedTasks.entrySet().stream() +// .filter(stringEntry -> stringEntry.getCount() > 1) +// .map(stringEntry -> stringEntry.getElement() + ": " + stringEntry.getCount()) +// .collect(Collectors.toList()); +// +// LOGGER.error("Found duplicate pushes: " + duplicates); +// } +// +// private Workflow getWorkflow(List randomTasks) { +// Workflow wf = new Workflow(); +// wf.setWorkflowId(randomTasks.get(0).getWorkflowInstanceId()); +// wf.setCorrelationId(wf.getWorkflowId()); +// wf.setTasks(randomTasks); +// WorkflowDef workflowDefinition = new WorkflowDef(); +// workflowDefinition.setName("abcd"); +// wf.setWorkflowDefinition(workflowDefinition); +// wf.setStartTime(System.currentTimeMillis()); +// return wf; +// } +// +// private List getRandomTasks(int i) { +// String timestamp = Long.toString(System.nanoTime()); +// return IntStream.range(0, i).mapToObj(j -> { +// String id = Thread.currentThread().getId() + "_" + timestamp + "_" + j; +// Task task = new Task(); +// task.setTaskId(id); +// task.setCorrelationId(Integer.toString(j)); +// task.setTaskType("taskType"); +// task.setReferenceTaskName("refName" + j); +// task.setWorkflowType("task_wf"); +// task.setWorkflowInstanceId(Thread.currentThread().getId() + "_" + timestamp); +// return task; +// }).collect(Collectors.toList()); +// } +// +// private List getRandomMessages(int i) { +// String timestamp = Long.toString(System.nanoTime()); +// return IntStream.range(0, i).mapToObj(j -> { +// String id = Thread.currentThread().getId() + "_" + timestamp + "_" + j; +// return new Message(id, "{ \"a\": \"b\", \"timestamp\": \" " + timestamp + " \"}", +// "receipt"); +// }).collect(Collectors.toList()); +// } +// +// private void flywayMigrate(DataSource dataSource) { +// FluentConfiguration flywayConfiguration = Flyway.configure() +// .table(configuration.getFlywayTable()) +// .locations(Paths.get("db","migration_postgres").toString()) +// .dataSource(dataSource) +// .placeholderReplacement(false); +// +// Flyway flyway = flywayConfiguration.load(); +// try { +// flyway.migrate(); +// } catch (FlywayException e) { +// if (e.getMessage().contains("non-empty")) { +// return; +// } +// throw e; +// } +// } +// +// public void resetAllData(DataSource dataSource) { +// // TODO +// } +// } diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java new file mode 100644 index 000000000..91041cd77 --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java @@ -0,0 +1,285 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.util; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class PostgresIndexQueryBuilderTest { + @Test + void shouldGenerateQueryForEmptyString() throws SQLException { + String inputQuery = ""; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals("SELECT json_data::TEXT FROM table_name LIMIT ? OFFSET ?", generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForNull() throws SQLException { + String inputQuery = null; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals("SELECT json_data::TEXT FROM table_name LIMIT ? OFFSET ?", generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForWorkflowId() throws SQLException { + String inputQuery = "workflowId=\"abc123\""; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE workflow_id = ? LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("abc123"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForMultipleInClause() throws SQLException { + String inputQuery = "status IN (COMPLETED,RUNNING)"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE status = ANY(?) LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter(new ArrayList<>(List.of("COMPLETED", "RUNNING"))); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForSingleInClause() throws SQLException { + String inputQuery = "status IN (COMPLETED)"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE status = ? LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("COMPLETED"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForStartTimeGt() throws SQLException { + String inputQuery = "startTime>1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE start_time > ?::TIMESTAMPTZ LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:54:58Z"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForStartTimeLt() throws SQLException { + String inputQuery = "startTime<1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE start_time < ?::TIMESTAMPTZ LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:54:58Z"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForUpdateTimeGt() throws SQLException { + String inputQuery = "updateTime>1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE update_time > ?::TIMESTAMPTZ LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:54:58Z"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForUpdateTimeLt() throws SQLException { + String inputQuery = "updateTime<1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE update_time < ?::TIMESTAMPTZ LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:54:58Z"); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateQueryForMultipleConditions() throws SQLException { + String inputQuery = + "workflowId=\"abc123\" AND workflowType IN (one,two) AND status IN (COMPLETED,RUNNING) AND startTime>1675701498000 AND startTime<1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String generatedQuery = builder.getQuery(); + assertEquals( + "SELECT json_data::TEXT FROM table_name WHERE start_time < ?::TIMESTAMPTZ AND start_time > ?::TIMESTAMPTZ AND status = ANY(?) AND workflow_id = ? AND workflow_type = ANY(?) LIMIT ? OFFSET ?", + generatedQuery); + Query mockQuery = mock(Query.class); + builder.addParameters(mockQuery); + InOrder inOrder = Mockito.inOrder(mockQuery); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:54:58Z"); + inOrder.verify(mockQuery).addParameter("2023-02-06T16:38:18Z"); + inOrder.verify(mockQuery).addParameter(new ArrayList<>(List.of("COMPLETED", "RUNNING"))); + inOrder.verify(mockQuery).addParameter("abc123"); + inOrder.verify(mockQuery).addParameter(new ArrayList<>(List.of("one", "two"))); + inOrder.verify(mockQuery).addParameter(15); + inOrder.verify(mockQuery).addParameter(0); + verifyNoMoreInteractions(mockQuery); + } + + @Test + void shouldGenerateOrderBy() throws SQLException { + String inputQuery = "updateTime<1675702498000"; + String[] query = {"updateTime:DESC"}; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, Arrays.asList(query)); + String expectedQuery = + "SELECT json_data::TEXT FROM table_name WHERE update_time < ?::TIMESTAMPTZ ORDER BY update_time DESC LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } + + @Test + void shouldGenerateOrderByMultiple() throws SQLException { + String inputQuery = "updateTime<1675702498000"; + String[] query = {"updateTime:DESC", "correlationId:ASC"}; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, Arrays.asList(query)); + String expectedQuery = + "SELECT json_data::TEXT FROM table_name WHERE update_time < ?::TIMESTAMPTZ ORDER BY update_time DESC, correlation_id ASC LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } + + @Test + void shouldNotAllowInvalidColumns() throws SQLException { + String inputQuery = "sqlInjection<1675702498000"; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, new ArrayList<>()); + String expectedQuery = "SELECT json_data::TEXT FROM table_name LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } + + @Test + void shouldNotAllowInvalidSortColumn() throws SQLException { + String inputQuery = "updateTime<1675702498000"; + String[] query = {"sqlInjection:DESC"}; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", inputQuery, "", 0, 15, Arrays.asList(query)); + String expectedQuery = + "SELECT json_data::TEXT FROM table_name WHERE update_time < ?::TIMESTAMPTZ LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } + + @Test + void shouldAllowFullTextSearch() throws SQLException { + String freeText = "correlation-id"; + String[] query = {"sqlInjection:DESC"}; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", "", freeText, 0, 15, Arrays.asList(query)); + String expectedQuery = + "SELECT json_data::TEXT FROM table_name WHERE to_tsvector(json_data::text) @@ to_tsquery(?) LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } + + @Test + void shouldAllowJsonSearch() throws SQLException { + String freeText = "{\"correlationId\":\"not-the-id\"}"; + String[] query = {"sqlInjection:DESC"}; + PostgresIndexQueryBuilder builder = + new PostgresIndexQueryBuilder( + "table_name", "", freeText, 0, 15, Arrays.asList(query)); + String expectedQuery = + "SELECT json_data::TEXT FROM table_name WHERE json_data @> ?::JSONB LIMIT ? OFFSET ?"; + assertEquals(expectedQuery, builder.getQuery()); + } +} diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java new file mode 100644 index 000000000..53cc2333e --- /dev/null +++ b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java @@ -0,0 +1,50 @@ +/* + *

+ * 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 com.netflix.conductor.test.integration.grpc.postgres; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.client.grpc.EventClient; +import com.netflix.conductor.client.grpc.MetadataClient; +import com.netflix.conductor.client.grpc.TaskClient; +import com.netflix.conductor.client.grpc.WorkflowClient; +import com.netflix.conductor.test.integration.grpc.AbstractGrpcEndToEndTest; + +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "conductor.db.type=postgres", + "conductor.app.asyncIndexingEnabled=false", + "conductor.elasticsearch.version=6", + "conductor.grpc-server.port=8098", + "conductor.indexing.type=elasticsearch", + "spring.datasource.url=jdbc:tc:postgresql:11.15-alpine:///conductor", // "tc" prefix + // starts the + // Postgres container + "spring.datasource.username=postgres", + "spring.datasource.password=postgres", + "spring.datasource.hikari.maximum-pool-size=8", + "spring.datasource.hikari.minimum-idle=300000" + }) +public class PostgresGrpcEndToEndTest extends AbstractGrpcEndToEndTest { + + @Before + public void init() { + taskClient = new TaskClient("localhost", 8098); + workflowClient = new WorkflowClient("localhost", 8098); + metadataClient = new MetadataClient("localhost", 8098); + eventClient = new EventClient("localhost", 8098); + } +} diff --git a/postgres-persistence/src/test/resources/application.properties b/postgres-persistence/src/test/resources/application.properties new file mode 100644 index 000000000..06ea54aa4 --- /dev/null +++ b/postgres-persistence/src/test/resources/application.properties @@ -0,0 +1,8 @@ +conductor.db.type=postgres + +spring.datasource.url=jdbc:tc:postgresql:11.15-alpine:///conductor +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.hikari.maximum-pool-size=8 +spring.datasource.hikari.auto-commit=false +spring.flyway.locations=classpath:db/migration_postgres diff --git a/settings.gradle b/settings.gradle index 40801041b..901c60278 100644 --- a/settings.gradle +++ b/settings.gradle @@ -64,6 +64,9 @@ include 'java-sdk' include 'workflow-event-listener' include 'test-util' include 'kafka' +include 'common-persistence' +include 'mysql-persistence' +include 'postgres-persistence' include 'test-harness' From cd160a2aba6e850adff00ac46f9911e80ecce181 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 18:43:42 -0800 Subject: [PATCH 036/202] metrics --- dependencies.gradle | 3 +- metrics/README.md | 31 +++ metrics/build.gradle | 30 +++ metrics/dependencies.lock | 255 ++++++++++++++++++ .../metrics/LoggingMetricsConfiguration.java | 85 ++++++ .../metrics/MetricsRegistryConfiguration.java | 46 ++++ .../PrometheusMetricsConfiguration.java | 46 ++++ .../LoggingMetricsConfigurationTest.java | 53 ++++ .../PrometheusMetricsConfigurationTest.java | 78 ++++++ settings.gradle | 1 + 10 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 metrics/README.md create mode 100644 metrics/build.gradle create mode 100644 metrics/dependencies.lock create mode 100644 metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java create mode 100644 metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java create mode 100644 metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java create mode 100644 metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java create mode 100644 metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java diff --git a/dependencies.gradle b/dependencies.gradle index 63c90e0be..65df86a3c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -59,5 +59,6 @@ ext { revFasterXml = '2.15.3' revAmqpClient = '5.13.0' revKafka = '2.6.0' - + revMicrometer = '1.6.2' + revPrometheus = '0.9.0' } diff --git a/metrics/README.md b/metrics/README.md new file mode 100644 index 000000000..fc482621b --- /dev/null +++ b/metrics/README.md @@ -0,0 +1,31 @@ +# Metrics +Conductor publishes detailed metrics for the server. +For the list of metrics published by server, see: + +https://netflix.github.io/conductor/metrics/server/ + +Conductor supports plugging in metrics collectors, the following are currently supported by this module: +1. Datadog +2. Prometheus +3. Logging (dumps all the metrics to Slf4J logger) + +## Published Artifacts + +Group: `com.netflix.conductor` + +| Published Artifact | Description | +| ----------- | ----------- | +| conductor-metrics | Metrics configuration | + +**Note**: If you are using `condutor-contribs` as a dependency, the metrics module is already included, you do not need to include it separately. + +#### Configuration +* Logging Metrics + + `conductor.metrics-logger.enabled=true` +* Prometheus + + `conductor.metrics-prometheus.enabled=true` +* Datadog + + `conductor.metrics-datadog.enabled=true` \ No newline at end of file diff --git a/metrics/build.gradle b/metrics/build.gradle new file mode 100644 index 000000000..4fdf59601 --- /dev/null +++ b/metrics/build.gradle @@ -0,0 +1,30 @@ +dependencies { + + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + + implementation "org.apache.commons:commons-lang3:" + + implementation "com.google.guava:guava:${revGuava}" + + implementation "javax.ws.rs:jsr311-api:${revJsr311Api}" + + implementation "io.reactivex:rxjava:${revRxJava}" + + implementation "com.netflix.spectator:spectator-reg-metrics3:${revSpectator}" + implementation "com.netflix.spectator:spectator-reg-micrometer:${revSpectator}" + implementation "io.prometheus:simpleclient:${revPrometheus}" + implementation "io.micrometer:micrometer-registry-prometheus:${revMicrometer}" + + implementation 'io.micrometer:micrometer-registry-datadog:1.9.1' + + + testImplementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation "org.testcontainers:mockserver:${revTestContainer}" + testImplementation "org.mock-server:mockserver-client-java:${revMockServerClient}" + + +} diff --git a/metrics/dependencies.lock b/metrics/dependencies.lock new file mode 100644 index 000000000..2b3af75aa --- /dev/null +++ b/metrics/dependencies.lock @@ -0,0 +1,255 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.netflix.spectator:spectator-reg-metrics3": { + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "locked": "0.122.0" + }, + "io.micrometer:micrometer-registry-datadog": { + "locked": "1.9.1" + }, + "io.micrometer:micrometer-registry-prometheus": { + "locked": "1.6.2" + }, + "io.prometheus:simpleclient": { + "locked": "0.9.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.netflix.spectator:spectator-reg-metrics3": { + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "locked": "0.122.0" + }, + "io.micrometer:micrometer-registry-datadog": { + "locked": "1.9.1" + }, + "io.micrometer:micrometer-registry-prometheus": { + "locked": "1.6.2" + }, + "io.prometheus:simpleclient": { + "locked": "0.9.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.netflix.spectator:spectator-reg-metrics3": { + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "locked": "0.122.0" + }, + "io.micrometer:micrometer-registry-datadog": { + "locked": "1.9.1" + }, + "io.micrometer:micrometer-registry-prometheus": { + "locked": "1.6.2" + }, + "io.prometheus:simpleclient": { + "locked": "0.9.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.mock-server:mockserver-client-java": { + "locked": "5.12.0" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + }, + "org.testcontainers:mockserver": { + "locked": "1.18.3" + } + }, + "testRuntimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.netflix.spectator:spectator-reg-metrics3": { + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "locked": "0.122.0" + }, + "io.micrometer:micrometer-registry-datadog": { + "locked": "1.9.1" + }, + "io.micrometer:micrometer-registry-prometheus": { + "locked": "1.6.2" + }, + "io.prometheus:simpleclient": { + "locked": "0.9.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "javax.ws.rs:jsr311-api": { + "locked": "1.1.1" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.mock-server:mockserver-client-java": { + "locked": "5.12.0" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + }, + "org.testcontainers:mockserver": { + "locked": "1.18.3" + } + } +} \ No newline at end of file diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java new file mode 100644 index 000000000..fb71cbc31 --- /dev/null +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.metrics; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; + +/** + * Metrics logging reporter, dumping all metrics into an Slf4J logger. + * + *

Enable in config: conductor.metrics-logger.enabled=true + * + *

additional config: conductor.metrics-logger.reportInterval=15s + */ +@ConditionalOnProperty(value = "conductor.metrics-logger.enabled", havingValue = "true") +@Configuration +public class LoggingMetricsConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingMetricsConfiguration.class); + + // Dedicated logger for metrics + // This way one can cleanly separate the metrics stream from rest of the logs + private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("ConductorMetrics"); + + @Value("${conductor.metrics-logger.reportInterval:#{T(java.time.Duration).ofSeconds(30)}}") + private Duration reportInterval; + + @Bean + public Slf4jReporter getSl4jReporter(MetricRegistry metricRegistry) { + return new Slf4jReporterProvider(metricRegistry, reportInterval.getSeconds()).getReporter(); + } + + static class Slf4jReporterProvider { + + private final long metricsReportInterval; + private final MetricRegistry metrics3Registry; + private final Logger logger; + + Slf4jReporterProvider(MetricRegistry metricRegistry, long reportInterval) { + this(metricRegistry, METRICS_LOGGER, reportInterval); + } + + Slf4jReporterProvider( + MetricRegistry metricRegistry, Logger outputLogger, long metricsReportInterval) { + this.metrics3Registry = metricRegistry; + this.logger = outputLogger; + this.metricsReportInterval = metricsReportInterval; + } + + public Slf4jReporter getReporter() { + final Slf4jReporter reporter = + Slf4jReporter.forRegistry(metrics3Registry) + .outputTo(logger) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + + reporter.start(metricsReportInterval, TimeUnit.SECONDS); + LOGGER.info( + "Logging metrics reporter started, reporting every {} seconds", + metricsReportInterval); + return reporter; + } + } +} diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java new file mode 100644 index 000000000..9b53ed523 --- /dev/null +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.metrics; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Spectator; +import com.netflix.spectator.metrics3.MetricsRegistry; + +import com.codahale.metrics.MetricRegistry; + +@ConditionalOnProperty(value = "conductor.metrics-logger.enabled", havingValue = "true") +@Configuration +public class MetricsRegistryConfiguration { + + public static final MetricRegistry METRIC_REGISTRY = new MetricRegistry(); + public static final MetricsRegistry METRICS_REGISTRY = + new MetricsRegistry(Clock.SYSTEM, METRIC_REGISTRY); + + static { + Spectator.globalRegistry().add(METRICS_REGISTRY); + } + + @Bean + public MetricRegistry metricRegistry() { + return METRIC_REGISTRY; + } + + @Bean + public MetricsRegistry metricsRegistry() { + return METRICS_REGISTRY; + } +} diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java new file mode 100644 index 000000000..a610f7978 --- /dev/null +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.metrics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; + +import com.netflix.spectator.api.Spectator; +import com.netflix.spectator.micrometer.MicrometerRegistry; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.prometheus.PrometheusRenameFilter; + +/** + * Metrics prometheus module, sending all metrics to a Prometheus server. + * + *

Enable in config: conductor.metrics-prometheus.enabled=true + * + *

Make sure your dependencies include both spectator-reg-micrometer & + * spring-boot-starter-actuator + */ +@ConditionalOnProperty(value = "conductor.metrics-prometheus.enabled", havingValue = "true") +@Configuration +public class PrometheusMetricsConfiguration { + private static final Logger LOGGER = + LoggerFactory.getLogger(PrometheusMetricsConfiguration.class); + + public PrometheusMetricsConfiguration(MeterRegistry meterRegistry) { + LOGGER.info("Prometheus metrics module initialized"); + final MicrometerRegistry metricsRegistry = new MicrometerRegistry(meterRegistry); + meterRegistry.config().meterFilter(new PrometheusRenameFilter()); + Spectator.globalRegistry().add(metricsRegistry); + } +} diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java new file mode 100644 index 000000000..333af5f4f --- /dev/null +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.metrics; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.conductor.contribs.metrics.LoggingMetricsConfiguration.Slf4jReporterProvider; + +import com.codahale.metrics.MetricRegistry; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +@RunWith(SpringRunner.class) +@Import({LoggingMetricsConfiguration.class, MetricsRegistryConfiguration.class}) +@TestPropertySource(properties = {"conductor.metrics-logger.enabled=true"}) +public class LoggingMetricsConfigurationTest { + + @Autowired MetricRegistry metricRegistry; + + @Test + public void testCollector() { + Logger logger = spy(Logger.class); + doReturn(true).when(logger).isInfoEnabled(any()); + Slf4jReporterProvider reporterProvider = + new Slf4jReporterProvider(metricRegistry, logger, 1); + metricRegistry.counter("test").inc(); + + reporterProvider.getReporter(); + verify(logger, timeout(TimeUnit.SECONDS.toMillis(10))).isInfoEnabled(null); + } +} diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java new file mode 100644 index 000000000..0a7324ada --- /dev/null +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.metrics; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.netflix.spectator.api.Registry; +import com.netflix.spectator.api.Spectator; +import com.netflix.spectator.micrometer.MicrometerRegistry; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; + +import static org.junit.Assert.assertTrue; + +@RunWith(SpringRunner.class) +@Import({PrometheusMetricsConfiguration.class}) +@TestPropertySource(properties = {"conductor.metrics-prometheus.enabled=true"}) +public class PrometheusMetricsConfigurationTest { + + @SuppressWarnings("unchecked") + @Test + public void testCollector() throws IllegalAccessException { + final Optional registries = + Arrays.stream(Spectator.globalRegistry().getClass().getDeclaredFields()) + .filter(f -> f.getName().equals("registries")) + .findFirst(); + assertTrue(registries.isPresent()); + registries.get().setAccessible(true); + + List meters = (List) registries.get().get(Spectator.globalRegistry()); + assertTrue(meters.size() > 0); + Optional microMeterReg = + meters.stream() + .filter(r -> r.getClass().equals(MicrometerRegistry.class)) + .findFirst(); + assertTrue(microMeterReg.isPresent()); + } + + @TestConfiguration + public static class TestConfig { + + /** + * This bean will be injected in PrometheusMetricsConfiguration, which wraps it with a + * MicrometerRegistry, and appends it to the global registry. + * + * @return a Prometheus registry instance + */ + @Bean + @Primary + public MeterRegistry meterRegistry() { + return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } + } +} diff --git a/settings.gradle b/settings.gradle index 901c60278..8ecbf9288 100644 --- a/settings.gradle +++ b/settings.gradle @@ -67,6 +67,7 @@ include 'kafka' include 'common-persistence' include 'mysql-persistence' include 'postgres-persistence' +include 'metrics' include 'test-harness' From e31f8bf90e24822a78f8cc0dd9130da11903abc7 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 18:48:20 -0800 Subject: [PATCH 037/202] Elasticsearch 7 --- dependencies.gradle | 1 + es7-persistence/README.md | 95 ++ es7-persistence/build.gradle | 54 + es7-persistence/dependencies.lock | 325 ++++ .../es7/config/ElasticSearchConditions.java | 42 + .../es7/config/ElasticSearchProperties.java | 228 +++ .../config/ElasticSearchV7Configuration.java | 110 ++ .../dao/index/BulkRequestBuilderWrapper.java | 55 + .../es7/dao/index/BulkRequestWrapper.java | 51 + .../es7/dao/index/ElasticSearchBaseDAO.java | 90 ++ .../es7/dao/index/ElasticSearchRestDAOV7.java | 1344 +++++++++++++++++ .../es7/dao/query/parser/Expression.java | 118 ++ .../es7/dao/query/parser/FilterProvider.java | 26 + .../dao/query/parser/GroupedExpression.java | 60 + .../es7/dao/query/parser/NameValue.java | 139 ++ .../query/parser/internal/AbstractNode.java | 178 +++ .../dao/query/parser/internal/BooleanOp.java | 57 + .../query/parser/internal/ComparisonOp.java | 102 ++ .../dao/query/parser/internal/ConstValue.java | 141 ++ .../internal/FunctionThrowingException.java | 22 + .../dao/query/parser/internal/ListConst.java | 70 + .../es7/dao/query/parser/internal/Name.java | 41 + .../parser/internal/ParserException.java | 28 + .../es7/dao/query/parser/internal/Range.java | 80 + .../main/resources/mappings_docType_task.json | 66 + .../resources/mappings_docType_workflow.json | 72 + .../src/main/resources/template_event.json | 48 + .../src/main/resources/template_message.json | 28 + .../src/main/resources/template_task_log.json | 24 + .../index/ElasticSearchRestDaoBaseTest.java | 74 + .../es7/dao/index/ElasticSearchTest.java | 66 + .../index/TestBulkRequestBuilderWrapper.java | 50 + .../dao/index/TestElasticSearchRestDAOV7.java | 523 +++++++ .../TestElasticSearchRestDAOV7Batch.java | 81 + .../es7/dao/query/parser/TestExpression.java | 149 ++ .../query/parser/TestGroupedExpression.java | 24 + .../parser/internal/AbstractParserTest.java | 27 + .../query/parser/internal/TestBooleanOp.java | 44 + .../parser/internal/TestComparisonOp.java | 44 + .../query/parser/internal/TestConstValue.java | 101 ++ .../dao/query/parser/internal/TestName.java | 33 + .../conductor/es7/utils/TestUtils.java | 76 + .../resources/expected_template_task_log.json | 24 + .../src/test/resources/task_summary.json | 17 + .../src/test/resources/workflow_summary.json | 12 + settings.gradle | 1 + 46 files changed, 5041 insertions(+) create mode 100644 es7-persistence/README.md create mode 100644 es7-persistence/build.gradle create mode 100644 es7-persistence/dependencies.lock create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java create mode 100644 es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java create mode 100644 es7-persistence/src/main/resources/mappings_docType_task.json create mode 100644 es7-persistence/src/main/resources/mappings_docType_workflow.json create mode 100644 es7-persistence/src/main/resources/template_event.json create mode 100644 es7-persistence/src/main/resources/template_message.json create mode 100644 es7-persistence/src/main/resources/template_task_log.json create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java create mode 100644 es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java create mode 100644 es7-persistence/src/test/resources/expected_template_task_log.json create mode 100644 es7-persistence/src/test/resources/task_summary.json create mode 100644 es7-persistence/src/test/resources/workflow_summary.json diff --git a/dependencies.gradle b/dependencies.gradle index 65df86a3c..42753f3ad 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -61,4 +61,5 @@ ext { revKafka = '2.6.0' revMicrometer = '1.6.2' revPrometheus = '0.9.0' + revElasticSearch7 = '7.17.13' } diff --git a/es7-persistence/README.md b/es7-persistence/README.md new file mode 100644 index 000000000..85143daad --- /dev/null +++ b/es7-persistence/README.md @@ -0,0 +1,95 @@ +# ES7 Persistence + +This module provides ES7 persistence when indexing workflows and tasks. + +### ES Breaking changes + +From ES6 to ES7 there were significant breaking changes which affected ES7-persistence module implementation. +* Mapping type deprecation +* Templates API +* TransportClient deprecation + +More information can be found here: https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes-7.0.html + + +## Build + +1. In order to use the ES7, you must change the following files from ES6 to ES7: + +https://github.com/Netflix/conductor/blob/main/build.gradle +https://github.com/Netflix/conductor/blob/main/server/src/main/resources/application.properties + +In file: + +- /build.gradle + +change ext['elasticsearch.version'] from revElasticSearch6 to revElasticSearch7 + + +In file: + +- /server/src/main/resources/application.properties + +change conductor.elasticsearch.version from 6 to 7 + +Also you need to recreate dependencies.lock files with ES7 dependencies. To do that delete all dependencies.lock files and then run: + +``` +./gradlew generateLock updateLock saveLock +``` + + +2. To use the ES7 for all modules include test-harness, you must change also the following files: + +https://github.com/Netflix/conductor/blob/main/test-harness/build.gradle +https://github.com/Netflix/conductor/blob/main/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java + +In file: + +- /test-harness/build.gradle + +* change module inclusion from 'es6-persistence' to 'es7-persistence' + +In file: + +- /test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java + +* change conductor.elasticsearch.version from 6 to 7 +* change DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch-oss").withTag("6.8.12") to DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch-oss").withTag("7.6.2") + + + +### Configuration +(Default values shown below) + +This module uses the following configuration options: +```properties +# A comma separated list of schema/host/port of the ES nodes to communicate with. +# Schema can be `http` or `https`. If schema is ignored then `http` transport will be used; +# Since ES deprecated TransportClient, conductor will use only the REST transport protocol. +conductor.elasticsearch.url= + +#The name of the workflow and task index. +conductor.elasticsearch.indexPrefix=conductor + +#Worker Queue size used in executor service for async methods in IndexDao. +conductor.elasticsearch.asyncWorkerQueueSize=100 + +#Maximum thread pool size in executor service for async methods in IndexDao +conductor.elasticsearch.asyncMaxPoolSize=12 + +#Timeout (in seconds) for the in-memory to be flushed if not explicitly indexed +conductor.elasticsearch.asyncBufferFlushTimeout=10 +``` + + +### BASIC Authentication +If you need to pass user/password to connect to ES, add the following properties to your config file +* conductor.elasticsearch.username +* conductor.elasticsearch.password + +Example +``` +conductor.elasticsearch.username=someusername +conductor.elasticsearch.password=somepassword +``` diff --git a/es7-persistence/build.gradle b/es7-persistence/build.gradle new file mode 100644 index 000000000..fdb652950 --- /dev/null +++ b/es7-persistence/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'java' +} + +configurations { + // Prevent shaded dependencies from being published, while keeping them available to tests + shadow.extendsFrom compileOnly + testRuntime.extendsFrom compileOnly +} + +ext['elasticsearch.version'] = revElasticSearch7 + +dependencies { + + implementation project(':conductor-common') + implementation project(':conductor-core') + implementation project(':conductor-common-persistence') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.retry:spring-retry' + + implementation "commons-io:commons-io:${revCommonsIo}" + implementation "org.apache.commons:commons-lang3" + implementation "com.google.guava:guava:${revGuava}" + + implementation "com.fasterxml.jackson.core:jackson-databind" + implementation "com.fasterxml.jackson.core:jackson-core" + + implementation "org.elasticsearch.client:elasticsearch-rest-client:${revElasticSearch7}" + implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:${revElasticSearch7}" + + testImplementation "net.java.dev.jna:jna:5.7.0" + testImplementation "org.awaitility:awaitility:${revAwaitility}" + testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" + testImplementation project(':conductor-test-util').sourceSets.test.output + testImplementation 'org.springframework.retry:spring-retry' + +} + +// Drop the classifier and delete jar task actions to replace the regular jar artifact with the shadow artifact +shadowJar { + configurations = [project.configurations.shadow] + classifier = null + + // Service files are not included by default. + mergeServiceFiles { + include 'META-INF/services/*' + include 'META-INF/maven/*' + } +} + +jar.enabled = false +jar.dependsOn shadowJar diff --git a/es7-persistence/dependencies.lock b/es7-persistence/dependencies.lock new file mode 100644 index 000000000..9411dbc82 --- /dev/null +++ b/es7-persistence/dependencies.lock @@ -0,0 +1,325 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-io:commons-io": { + "locked": "2.7" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "7.17.15" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "7.17.15" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.retry:spring-retry": { + "locked": "1.3.4" + } + }, + "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.13.5" + }, + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.15.0" + }, + "commons-io:commons-io": { + "locked": "2.7" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "7.17.15" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "7.17.15" + } + }, + "shadow": { + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.retry:spring-retry": { + "locked": "1.3.4" + } + }, + "testCompileClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "locked": "2.13.5" + }, + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-io:commons-io": { + "locked": "2.7" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.java.dev.jna:jna": { + "locked": "5.7.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.awaitility:awaitility": { + "locked": "3.1.6" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "7.17.15" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "7.17.15" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.retry:spring-retry": { + "locked": "1.3.4" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.18.3" + } + }, + "testRuntime": { + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.retry:spring-retry": { + "locked": "1.3.4" + } + }, + "testRuntimeClasspath": { + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.13.5" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.13.5" + }, + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-common-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.15.0" + }, + "commons-io:commons-io": { + "locked": "2.7" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "net.java.dev.jna:jna": { + "locked": "5.7.0" + }, + "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common-persistence" + ], + "locked": "2.17.2" + }, + "org.awaitility:awaitility": { + "locked": "3.1.6" + }, + "org.elasticsearch.client:elasticsearch-rest-client": { + "locked": "7.17.15" + }, + "org.elasticsearch.client:elasticsearch-rest-high-level-client": { + "locked": "7.17.15" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.retry:spring-retry": { + "locked": "1.3.4" + }, + "org.testcontainers:elasticsearch": { + "locked": "1.18.3" + } + } +} \ No newline at end of file diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java new file mode 100644 index 000000000..9875f1776 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.config; + +import org.springframework.boot.autoconfigure.condition.AllNestedConditions; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +public class ElasticSearchConditions { + + private ElasticSearchConditions() {} + + public static class ElasticSearchV7Enabled extends AllNestedConditions { + + ElasticSearchV7Enabled() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @SuppressWarnings("unused") + @ConditionalOnProperty( + name = "conductor.indexing.enabled", + havingValue = "true", + matchIfMissing = true) + static class enabledIndexing {} + + @SuppressWarnings("unused") + @ConditionalOnProperty( + name = "conductor.elasticsearch.version", + havingValue = "7", + matchIfMissing = true) + static class enabledES7 {} + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java new file mode 100644 index 000000000..d4ed352a7 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java @@ -0,0 +1,228 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.config; + +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; + +@ConfigurationProperties("conductor.elasticsearch") +public class ElasticSearchProperties { + + /** + * The comma separated list of urls for the elasticsearch cluster. Format -- + * host1:port1,host2:port2 + */ + private String url = "localhost:9300"; + + /** The index prefix to be used when creating indices */ + private String indexPrefix = "conductor"; + + /** The color of the elasticserach cluster to wait for to confirm healthy status */ + private String clusterHealthColor = "green"; + + /** The size of the batch to be used for bulk indexing in async mode */ + private int indexBatchSize = 1; + + /** The size of the queue used for holding async indexing tasks */ + private int asyncWorkerQueueSize = 100; + + /** The maximum number of threads allowed in the async pool */ + private int asyncMaxPoolSize = 12; + + /** + * The time in seconds after which the async buffers will be flushed (if no activity) to prevent + * data loss + */ + @DurationUnit(ChronoUnit.SECONDS) + private Duration asyncBufferFlushTimeout = Duration.ofSeconds(10); + + /** The number of shards that the index will be created with */ + private int indexShardCount = 5; + + /** The number of replicas that the index will be configured to have */ + private int indexReplicasCount = 1; + + /** The number of task log results that will be returned in the response */ + private int taskLogResultLimit = 10; + + /** The timeout in milliseconds used when requesting a connection from the connection manager */ + private int restClientConnectionRequestTimeout = -1; + + /** Used to control if index management is to be enabled or will be controlled externally */ + private boolean autoIndexManagementEnabled = true; + + /** + * Document types are deprecated in ES6 and removed from ES7. This property can be used to + * disable the use of specific document types with an override. This property is currently used + * in ES6 module. + * + *

Note that this property will only take effect if {@link + * ElasticSearchProperties#isAutoIndexManagementEnabled} is set to false and index management is + * handled outside of this module. + */ + private String documentTypeOverride = ""; + + /** Elasticsearch basic auth username */ + private String username; + + /** Elasticsearch basic auth password */ + private String password; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getIndexPrefix() { + return indexPrefix; + } + + public void setIndexPrefix(String indexPrefix) { + this.indexPrefix = indexPrefix; + } + + public String getClusterHealthColor() { + return clusterHealthColor; + } + + public void setClusterHealthColor(String clusterHealthColor) { + this.clusterHealthColor = clusterHealthColor; + } + + public int getIndexBatchSize() { + return indexBatchSize; + } + + public void setIndexBatchSize(int indexBatchSize) { + this.indexBatchSize = indexBatchSize; + } + + public int getAsyncWorkerQueueSize() { + return asyncWorkerQueueSize; + } + + public void setAsyncWorkerQueueSize(int asyncWorkerQueueSize) { + this.asyncWorkerQueueSize = asyncWorkerQueueSize; + } + + public int getAsyncMaxPoolSize() { + return asyncMaxPoolSize; + } + + public void setAsyncMaxPoolSize(int asyncMaxPoolSize) { + this.asyncMaxPoolSize = asyncMaxPoolSize; + } + + public Duration getAsyncBufferFlushTimeout() { + return asyncBufferFlushTimeout; + } + + public void setAsyncBufferFlushTimeout(Duration asyncBufferFlushTimeout) { + this.asyncBufferFlushTimeout = asyncBufferFlushTimeout; + } + + public int getIndexShardCount() { + return indexShardCount; + } + + public void setIndexShardCount(int indexShardCount) { + this.indexShardCount = indexShardCount; + } + + public int getIndexReplicasCount() { + return indexReplicasCount; + } + + public void setIndexReplicasCount(int indexReplicasCount) { + this.indexReplicasCount = indexReplicasCount; + } + + public int getTaskLogResultLimit() { + return taskLogResultLimit; + } + + public void setTaskLogResultLimit(int taskLogResultLimit) { + this.taskLogResultLimit = taskLogResultLimit; + } + + public int getRestClientConnectionRequestTimeout() { + return restClientConnectionRequestTimeout; + } + + public void setRestClientConnectionRequestTimeout(int restClientConnectionRequestTimeout) { + this.restClientConnectionRequestTimeout = restClientConnectionRequestTimeout; + } + + public boolean isAutoIndexManagementEnabled() { + return autoIndexManagementEnabled; + } + + public void setAutoIndexManagementEnabled(boolean autoIndexManagementEnabled) { + this.autoIndexManagementEnabled = autoIndexManagementEnabled; + } + + public String getDocumentTypeOverride() { + return documentTypeOverride; + } + + public void setDocumentTypeOverride(String documentTypeOverride) { + this.documentTypeOverride = documentTypeOverride; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List toURLs() { + String clusterAddress = getUrl(); + String[] hosts = clusterAddress.split(","); + return Arrays.stream(hosts) + .map( + host -> + (host.startsWith("http://") || host.startsWith("https://")) + ? toURL(host) + : toURL("http://" + host)) + .collect(Collectors.toList()); + } + + private URL toURL(String url) { + try { + return new URL(url); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(url + "can not be converted to java.net.URL"); + } + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java new file mode 100644 index 000000000..3bbdb0fab --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.config; + +import java.net.URL; +import java.util.List; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.dao.IndexDAO; +import com.netflix.conductor.es7.dao.index.ElasticSearchRestDAOV7; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(ElasticSearchProperties.class) +@Conditional(ElasticSearchConditions.ElasticSearchV7Enabled.class) +public class ElasticSearchV7Configuration { + + private static final Logger log = LoggerFactory.getLogger(ElasticSearchV7Configuration.class); + + @Bean + public RestClient restClient(ElasticSearchProperties properties) { + RestClientBuilder restClientBuilder = + RestClient.builder(convertToHttpHosts(properties.toURLs())); + if (properties.getRestClientConnectionRequestTimeout() > 0) { + restClientBuilder.setRequestConfigCallback( + requestConfigBuilder -> + requestConfigBuilder.setConnectionRequestTimeout( + properties.getRestClientConnectionRequestTimeout())); + } + return restClientBuilder.build(); + } + + @Bean + public RestClientBuilder restClientBuilder(ElasticSearchProperties properties) { + RestClientBuilder builder = RestClient.builder(convertToHttpHosts(properties.toURLs())); + + if (properties.getUsername() != null && properties.getPassword() != null) { + log.info( + "Configure ElasticSearch with BASIC authentication. User:{}", + properties.getUsername()); + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials( + properties.getUsername(), properties.getPassword())); + builder.setHttpClientConfigCallback( + httpClientBuilder -> + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); + } else { + log.info("Configure ElasticSearch with no authentication."); + } + return builder; + } + + @Primary // If you are including this project, it's assumed you want ES to be your indexing + // mechanism + @Bean + public IndexDAO es7IndexDAO( + RestClientBuilder restClientBuilder, + @Qualifier("es7RetryTemplate") RetryTemplate retryTemplate, + ElasticSearchProperties properties, + ObjectMapper objectMapper) { + String url = properties.getUrl(); + return new ElasticSearchRestDAOV7( + restClientBuilder, retryTemplate, properties, objectMapper); + } + + @Bean + public RetryTemplate es7RetryTemplate() { + RetryTemplate retryTemplate = new RetryTemplate(); + FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); + fixedBackOffPolicy.setBackOffPeriod(1000L); + retryTemplate.setBackOffPolicy(fixedBackOffPolicy); + return retryTemplate; + } + + private HttpHost[] convertToHttpHosts(List hosts) { + return hosts.stream() + .map(host -> new HttpHost(host.getHost(), host.getPort(), host.getProtocol())) + .toArray(HttpHost[]::new); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java new file mode 100644 index 000000000..a41f38568 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.util.Objects; + +import org.elasticsearch.action.ActionFuture; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.update.UpdateRequest; +import org.springframework.lang.NonNull; + +/** Thread-safe wrapper for {@link BulkRequestBuilder}. */ +public class BulkRequestBuilderWrapper { + private final BulkRequestBuilder bulkRequestBuilder; + + public BulkRequestBuilderWrapper(@NonNull BulkRequestBuilder bulkRequestBuilder) { + this.bulkRequestBuilder = Objects.requireNonNull(bulkRequestBuilder); + } + + public void add(@NonNull UpdateRequest req) { + synchronized (bulkRequestBuilder) { + bulkRequestBuilder.add(Objects.requireNonNull(req)); + } + } + + public void add(@NonNull IndexRequest req) { + synchronized (bulkRequestBuilder) { + bulkRequestBuilder.add(Objects.requireNonNull(req)); + } + } + + public int numberOfActions() { + synchronized (bulkRequestBuilder) { + return bulkRequestBuilder.numberOfActions(); + } + } + + public ActionFuture execute() { + synchronized (bulkRequestBuilder) { + return bulkRequestBuilder.execute(); + } + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java new file mode 100644 index 000000000..66d3a26b4 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.util.Objects; + +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.update.UpdateRequest; +import org.springframework.lang.NonNull; + +/** Thread-safe wrapper for {@link BulkRequest}. */ +class BulkRequestWrapper { + private final BulkRequest bulkRequest; + + BulkRequestWrapper(@NonNull BulkRequest bulkRequest) { + this.bulkRequest = Objects.requireNonNull(bulkRequest); + } + + public void add(@NonNull UpdateRequest req) { + synchronized (bulkRequest) { + bulkRequest.add(Objects.requireNonNull(req)); + } + } + + public void add(@NonNull IndexRequest req) { + synchronized (bulkRequest) { + bulkRequest.add(Objects.requireNonNull(req)); + } + } + + BulkRequest get() { + return bulkRequest; + } + + int numberOfActions() { + synchronized (bulkRequest) { + return bulkRequest.numberOfActions(); + } + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java new file mode 100644 index 000000000..6abb4c742 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.io.IOException; +import java.util.ArrayList; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.QueryStringQueryBuilder; + +import com.netflix.conductor.dao.IndexDAO; +import com.netflix.conductor.es7.dao.query.parser.Expression; +import com.netflix.conductor.es7.dao.query.parser.internal.ParserException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +abstract class ElasticSearchBaseDAO implements IndexDAO { + + String indexPrefix; + ObjectMapper objectMapper; + + String loadTypeMappingSource(String path) throws IOException { + return applyIndexPrefixToTemplate( + IOUtils.toString(ElasticSearchBaseDAO.class.getResourceAsStream(path))); + } + + private String applyIndexPrefixToTemplate(String text) throws JsonProcessingException { + String indexPatternsFieldName = "index_patterns"; + JsonNode root = objectMapper.readTree(text); + if (root != null) { + JsonNode indexPatternsNodeValue = root.get(indexPatternsFieldName); + if (indexPatternsNodeValue != null && indexPatternsNodeValue.isArray()) { + ArrayList patternsWithPrefix = new ArrayList<>(); + indexPatternsNodeValue.forEach( + v -> { + String patternText = v.asText(); + StringBuilder sb = new StringBuilder(); + if (patternText.startsWith("*")) { + sb.append("*") + .append(indexPrefix) + .append("_") + .append(patternText.substring(1)); + } else { + sb.append(indexPrefix).append("_").append(patternText); + } + patternsWithPrefix.add(sb.toString()); + }); + ((ObjectNode) root) + .set(indexPatternsFieldName, objectMapper.valueToTree(patternsWithPrefix)); + System.out.println( + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(root); + } + } + return text; + } + + BoolQueryBuilder boolQueryBuilder(String expression, String queryString) + throws ParserException { + QueryBuilder queryBuilder = QueryBuilders.matchAllQuery(); + if (StringUtils.isNotEmpty(expression)) { + Expression exp = Expression.fromString(expression); + queryBuilder = exp.getFilterBuilder(); + } + BoolQueryBuilder filterQuery = QueryBuilders.boolQuery().must(queryBuilder); + QueryStringQueryBuilder stringQuery = QueryBuilders.queryStringQuery(queryString); + return QueryBuilders.boolQuery().must(stringQuery).must(filterQuery); + } + + protected String getIndexName(String documentType) { + return indexPrefix + "_" + documentType; + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java new file mode 100644 index 000000000..05491872d --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java @@ -0,0 +1,1344 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NByteArrayEntity; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.delete.DeleteRequest; +import org.elasticsearch.action.delete.DeleteResponse; +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.client.*; +import org.elasticsearch.client.core.CountRequest; +import org.elasticsearch.client.core.CountResponse; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.FieldSortBuilder; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.xcontent.XContentType; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.retry.support.RetryTemplate; + +import com.netflix.conductor.annotations.Trace; +import com.netflix.conductor.common.metadata.events.EventExecution; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.core.exception.TransientException; +import com.netflix.conductor.dao.IndexDAO; +import com.netflix.conductor.es7.config.ElasticSearchProperties; +import com.netflix.conductor.es7.dao.query.parser.internal.ParserException; +import com.netflix.conductor.metrics.Monitors; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.type.MapType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import jakarta.annotation.*; + +@Trace +public class ElasticSearchRestDAOV7 extends ElasticSearchBaseDAO implements IndexDAO { + + private static final Logger logger = LoggerFactory.getLogger(ElasticSearchRestDAOV7.class); + + private static final String CLASS_NAME = ElasticSearchRestDAOV7.class.getSimpleName(); + + private static final int CORE_POOL_SIZE = 6; + private static final long KEEP_ALIVE_TIME = 1L; + + private static final String WORKFLOW_DOC_TYPE = "workflow"; + private static final String TASK_DOC_TYPE = "task"; + private static final String LOG_DOC_TYPE = "task_log"; + private static final String EVENT_DOC_TYPE = "event"; + private static final String MSG_DOC_TYPE = "message"; + + private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMWW"); + + private @interface HttpMethod { + + String GET = "GET"; + String POST = "POST"; + String PUT = "PUT"; + String HEAD = "HEAD"; + } + + private static final String className = ElasticSearchRestDAOV7.class.getSimpleName(); + + private final String workflowIndexName; + private final String taskIndexName; + private final String eventIndexPrefix; + private String eventIndexName; + private final String messageIndexPrefix; + private String messageIndexName; + private String logIndexName; + private final String logIndexPrefix; + + private final String clusterHealthColor; + private final RestHighLevelClient elasticSearchClient; + private final RestClient elasticSearchAdminClient; + private final ExecutorService executorService; + private final ExecutorService logExecutorService; + private final ConcurrentHashMap bulkRequests; + private final int indexBatchSize; + private final int asyncBufferFlushTimeout; + private final ElasticSearchProperties properties; + private final RetryTemplate retryTemplate; + + static { + SIMPLE_DATE_FORMAT.setTimeZone(GMT); + } + + public ElasticSearchRestDAOV7( + RestClientBuilder restClientBuilder, + RetryTemplate retryTemplate, + ElasticSearchProperties properties, + ObjectMapper objectMapper) { + + this.objectMapper = objectMapper; + this.elasticSearchAdminClient = restClientBuilder.build(); + this.elasticSearchClient = new RestHighLevelClient(restClientBuilder); + this.clusterHealthColor = properties.getClusterHealthColor(); + this.bulkRequests = new ConcurrentHashMap<>(); + this.indexBatchSize = properties.getIndexBatchSize(); + this.asyncBufferFlushTimeout = (int) properties.getAsyncBufferFlushTimeout().getSeconds(); + this.properties = properties; + + this.indexPrefix = properties.getIndexPrefix(); + + this.workflowIndexName = getIndexName(WORKFLOW_DOC_TYPE); + this.taskIndexName = getIndexName(TASK_DOC_TYPE); + this.logIndexPrefix = this.indexPrefix + "_" + LOG_DOC_TYPE; + this.messageIndexPrefix = this.indexPrefix + "_" + MSG_DOC_TYPE; + this.eventIndexPrefix = this.indexPrefix + "_" + EVENT_DOC_TYPE; + int workerQueueSize = properties.getAsyncWorkerQueueSize(); + int maximumPoolSize = properties.getAsyncMaxPoolSize(); + + // Set up a workerpool for performing async operations. + this.executorService = + new ThreadPoolExecutor( + CORE_POOL_SIZE, + maximumPoolSize, + KEEP_ALIVE_TIME, + TimeUnit.MINUTES, + new LinkedBlockingQueue<>(workerQueueSize), + (runnable, executor) -> { + logger.warn( + "Request {} to async dao discarded in executor {}", + runnable, + executor); + Monitors.recordDiscardedIndexingCount("indexQueue"); + }); + + // Set up a workerpool for performing async operations for task_logs, event_executions, + // message + int corePoolSize = 1; + maximumPoolSize = 2; + long keepAliveTime = 30L; + this.logExecutorService = + new ThreadPoolExecutor( + corePoolSize, + maximumPoolSize, + keepAliveTime, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(workerQueueSize), + (runnable, executor) -> { + logger.warn( + "Request {} to async log dao discarded in executor {}", + runnable, + executor); + Monitors.recordDiscardedIndexingCount("logQueue"); + }); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate(this::flushBulkRequests, 60, 30, TimeUnit.SECONDS); + this.retryTemplate = retryTemplate; + } + + @PreDestroy + private void shutdown() { + logger.info("Gracefully shutdown executor service"); + shutdownExecutorService(logExecutorService); + shutdownExecutorService(executorService); + } + + private void shutdownExecutorService(ExecutorService execService) { + try { + execService.shutdown(); + if (execService.awaitTermination(30, TimeUnit.SECONDS)) { + logger.debug("tasks completed, shutting down"); + } else { + logger.warn("Forcing shutdown after waiting for 30 seconds"); + execService.shutdownNow(); + } + } catch (InterruptedException ie) { + logger.warn( + "Shutdown interrupted, invoking shutdownNow on scheduledThreadPoolExecutor for delay queue"); + execService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + @PostConstruct + public void setup() throws Exception { + waitForHealthyCluster(); + + if (properties.isAutoIndexManagementEnabled()) { + createIndexesTemplates(); + createWorkflowIndex(); + createTaskIndex(); + } + } + + private void createIndexesTemplates() { + try { + initIndexesTemplates(); + updateIndexesNames(); + Executors.newScheduledThreadPool(1) + .scheduleAtFixedRate(this::updateIndexesNames, 0, 1, TimeUnit.HOURS); + } catch (Exception e) { + logger.error("Error creating index templates!", e); + } + } + + private void initIndexesTemplates() { + initIndexTemplate(LOG_DOC_TYPE); + initIndexTemplate(EVENT_DOC_TYPE); + initIndexTemplate(MSG_DOC_TYPE); + } + + /** Initializes the index with the required templates and mappings. */ + private void initIndexTemplate(String type) { + String template = "template_" + type; + try { + if (doesResourceNotExist("/_template/" + template)) { + logger.info("Creating the index template '" + template + "'"); + InputStream stream = + ElasticSearchRestDAOV7.class.getResourceAsStream("/" + template + ".json"); + byte[] templateSource = IOUtils.toByteArray(stream); + + HttpEntity entity = + new NByteArrayEntity(templateSource, ContentType.APPLICATION_JSON); + Request request = new Request(HttpMethod.PUT, "/_template/" + template); + request.setEntity(entity); + String test = + IOUtils.toString( + elasticSearchAdminClient + .performRequest(request) + .getEntity() + .getContent()); + } + } catch (Exception e) { + logger.error("Failed to init " + template, e); + } + } + + private void updateIndexesNames() { + logIndexName = updateIndexName(LOG_DOC_TYPE); + eventIndexName = updateIndexName(EVENT_DOC_TYPE); + messageIndexName = updateIndexName(MSG_DOC_TYPE); + } + + private String updateIndexName(String type) { + String indexName = + this.indexPrefix + "_" + type + "_" + SIMPLE_DATE_FORMAT.format(new Date()); + try { + addIndex(indexName); + return indexName; + } catch (IOException e) { + logger.error("Failed to update log index name: {}", indexName, e); + throw new NonTransientException(e.getMessage(), e); + } + } + + private void createWorkflowIndex() { + String indexName = getIndexName(WORKFLOW_DOC_TYPE); + try { + addIndex(indexName, "/mappings_docType_workflow.json"); + } catch (IOException e) { + logger.error("Failed to initialize index '{}'", indexName, e); + } + } + + private void createTaskIndex() { + String indexName = getIndexName(TASK_DOC_TYPE); + try { + addIndex(indexName, "/mappings_docType_task.json"); + } catch (IOException e) { + logger.error("Failed to initialize index '{}'", indexName, e); + } + } + + /** + * Waits for the ES cluster to become green. + * + * @throws Exception If there is an issue connecting with the ES cluster. + */ + private void waitForHealthyCluster() throws Exception { + Map params = new HashMap<>(); + params.put("wait_for_status", this.clusterHealthColor); + params.put("timeout", "30s"); + Request request = new Request("GET", "/_cluster/health"); + request.addParameters(params); + elasticSearchAdminClient.performRequest(request); + } + + /** + * Adds an index to elasticsearch if it does not exist. + * + * @param index The name of the index to create. + * @param mappingFilename Index mapping filename + * @throws IOException If an error occurred during requests to ES. + */ + private void addIndex(String index, final String mappingFilename) throws IOException { + logger.info("Adding index '{}'...", index); + String resourcePath = "/" + index; + if (doesResourceNotExist(resourcePath)) { + try { + ObjectNode setting = objectMapper.createObjectNode(); + ObjectNode indexSetting = objectMapper.createObjectNode(); + ObjectNode root = objectMapper.createObjectNode(); + indexSetting.put("number_of_shards", properties.getIndexShardCount()); + indexSetting.put("number_of_replicas", properties.getIndexReplicasCount()); + JsonNode mappingNodeValue = + objectMapper.readTree(loadTypeMappingSource(mappingFilename)); + root.set("settings", indexSetting); + root.set("mappings", mappingNodeValue); + Request request = new Request(HttpMethod.PUT, resourcePath); + request.setEntity( + new NStringEntity( + objectMapper.writeValueAsString(root), + ContentType.APPLICATION_JSON)); + elasticSearchAdminClient.performRequest(request); + logger.info("Added '{}' index", index); + } catch (ResponseException e) { + + boolean errorCreatingIndex = true; + + Response errorResponse = e.getResponse(); + if (errorResponse.getStatusLine().getStatusCode() == HttpStatus.SC_BAD_REQUEST) { + JsonNode root = + objectMapper.readTree(EntityUtils.toString(errorResponse.getEntity())); + String errorCode = root.get("error").get("type").asText(); + if ("index_already_exists_exception".equals(errorCode)) { + errorCreatingIndex = false; + } + } + + if (errorCreatingIndex) { + throw e; + } + } + } else { + logger.info("Index '{}' already exists", index); + } + } + + /** + * Adds an index to elasticsearch if it does not exist. + * + * @param index The name of the index to create. + * @throws IOException If an error occurred during requests to ES. + */ + private void addIndex(final String index) throws IOException { + + logger.info("Adding index '{}'...", index); + + String resourcePath = "/" + index; + + if (doesResourceNotExist(resourcePath)) { + + try { + ObjectNode setting = objectMapper.createObjectNode(); + ObjectNode indexSetting = objectMapper.createObjectNode(); + + indexSetting.put("number_of_shards", properties.getIndexShardCount()); + indexSetting.put("number_of_replicas", properties.getIndexReplicasCount()); + + setting.set("settings", indexSetting); + + Request request = new Request(HttpMethod.PUT, resourcePath); + request.setEntity( + new NStringEntity(setting.toString(), ContentType.APPLICATION_JSON)); + elasticSearchAdminClient.performRequest(request); + logger.info("Added '{}' index", index); + } catch (ResponseException e) { + + boolean errorCreatingIndex = true; + + Response errorResponse = e.getResponse(); + if (errorResponse.getStatusLine().getStatusCode() == HttpStatus.SC_BAD_REQUEST) { + JsonNode root = + objectMapper.readTree(EntityUtils.toString(errorResponse.getEntity())); + String errorCode = root.get("error").get("type").asText(); + if ("index_already_exists_exception".equals(errorCode)) { + errorCreatingIndex = false; + } + } + + if (errorCreatingIndex) { + throw e; + } + } + } else { + logger.info("Index '{}' already exists", index); + } + } + + /** + * Adds a mapping type to an index if it does not exist. + * + * @param index The name of the index. + * @param mappingType The name of the mapping type. + * @param mappingFilename The name of the mapping file to use to add the mapping if it does not + * exist. + * @throws IOException If an error occurred during requests to ES. + */ + private void addMappingToIndex( + final String index, final String mappingType, final String mappingFilename) + throws IOException { + + logger.info("Adding '{}' mapping to index '{}'...", mappingType, index); + + String resourcePath = "/" + index + "/_mapping"; + + if (doesResourceNotExist(resourcePath)) { + HttpEntity entity = + new NByteArrayEntity( + loadTypeMappingSource(mappingFilename).getBytes(), + ContentType.APPLICATION_JSON); + Request request = new Request(HttpMethod.PUT, resourcePath); + request.setEntity(entity); + elasticSearchAdminClient.performRequest(request); + logger.info("Added '{}' mapping", mappingType); + } else { + logger.info("Mapping '{}' already exists", mappingType); + } + } + + /** + * Determines whether a resource exists in ES. This will call a GET method to a particular path + * and return true if status 200; false otherwise. + * + * @param resourcePath The path of the resource to get. + * @return True if it exists; false otherwise. + * @throws IOException If an error occurred during requests to ES. + */ + public boolean doesResourceExist(final String resourcePath) throws IOException { + Request request = new Request(HttpMethod.HEAD, resourcePath); + Response response = elasticSearchAdminClient.performRequest(request); + return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK; + } + + /** + * The inverse of doesResourceExist. + * + * @param resourcePath The path of the resource to check. + * @return True if it does not exist; false otherwise. + * @throws IOException If an error occurred during requests to ES. + */ + public boolean doesResourceNotExist(final String resourcePath) throws IOException { + return !doesResourceExist(resourcePath); + } + + @Override + public void indexWorkflow(WorkflowSummary workflow) { + try { + long startTime = Instant.now().toEpochMilli(); + String workflowId = workflow.getWorkflowId(); + byte[] docBytes = objectMapper.writeValueAsBytes(workflow); + + IndexRequest request = + new IndexRequest(workflowIndexName) + .id(workflowId) + .source(docBytes, XContentType.JSON); + elasticSearchClient.index(request, RequestOptions.DEFAULT); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for indexing workflow: {}", endTime - startTime, workflowId); + Monitors.recordESIndexTime("index_workflow", WORKFLOW_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (Exception e) { + Monitors.error(className, "indexWorkflow"); + logger.error("Failed to index workflow: {}", workflow.getWorkflowId(), e); + } + } + + @Override + public CompletableFuture asyncIndexWorkflow(WorkflowSummary workflow) { + return CompletableFuture.runAsync(() -> indexWorkflow(workflow), executorService); + } + + @Override + public void indexTask(TaskSummary task) { + try { + long startTime = Instant.now().toEpochMilli(); + String taskId = task.getTaskId(); + + indexObject(taskIndexName, TASK_DOC_TYPE, taskId, task); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for indexing task:{} in workflow: {}", + endTime - startTime, + taskId, + task.getWorkflowId()); + Monitors.recordESIndexTime("index_task", TASK_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (Exception e) { + logger.error("Failed to index task: {}", task.getTaskId(), e); + } + } + + @Override + public CompletableFuture asyncIndexTask(TaskSummary task) { + return CompletableFuture.runAsync(() -> indexTask(task), executorService); + } + + @Override + public void addTaskExecutionLogs(List taskExecLogs) { + if (taskExecLogs.isEmpty()) { + return; + } + + long startTime = Instant.now().toEpochMilli(); + BulkRequest bulkRequest = new BulkRequest(); + for (TaskExecLog log : taskExecLogs) { + + byte[] docBytes; + try { + docBytes = objectMapper.writeValueAsBytes(log); + } catch (JsonProcessingException e) { + logger.error("Failed to convert task log to JSON for task {}", log.getTaskId()); + continue; + } + + IndexRequest request = new IndexRequest(logIndexName); + request.source(docBytes, XContentType.JSON); + bulkRequest.add(request); + } + + try { + elasticSearchClient.bulk(bulkRequest, RequestOptions.DEFAULT); + long endTime = Instant.now().toEpochMilli(); + logger.debug("Time taken {} for indexing taskExecutionLogs", endTime - startTime); + Monitors.recordESIndexTime( + "index_task_execution_logs", LOG_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "logQueue", ((ThreadPoolExecutor) logExecutorService).getQueue().size()); + } catch (Exception e) { + List taskIds = + taskExecLogs.stream().map(TaskExecLog::getTaskId).collect(Collectors.toList()); + logger.error("Failed to index task execution logs for tasks: {}", taskIds, e); + } + } + + @Override + public CompletableFuture asyncAddTaskExecutionLogs(List logs) { + return CompletableFuture.runAsync(() -> addTaskExecutionLogs(logs), logExecutorService); + } + + @Override + public List getTaskExecutionLogs(String taskId) { + try { + BoolQueryBuilder query = boolQueryBuilder("taskId='" + taskId + "'", "*"); + + // Create the searchObjectIdsViaExpression source + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(query); + searchSourceBuilder.sort(new FieldSortBuilder("createdTime").order(SortOrder.ASC)); + searchSourceBuilder.size(properties.getTaskLogResultLimit()); + + // Generate the actual request to send to ES. + SearchRequest searchRequest = new SearchRequest(logIndexPrefix + "*"); + searchRequest.source(searchSourceBuilder); + + SearchResponse response = + elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT); + + return mapTaskExecLogsResponse(response); + } catch (Exception e) { + logger.error("Failed to get task execution logs for task: {}", taskId, e); + } + return null; + } + + private List mapTaskExecLogsResponse(SearchResponse response) throws IOException { + SearchHit[] hits = response.getHits().getHits(); + List logs = new ArrayList<>(hits.length); + for (SearchHit hit : hits) { + String source = hit.getSourceAsString(); + TaskExecLog tel = objectMapper.readValue(source, TaskExecLog.class); + logs.add(tel); + } + return logs; + } + + @Override + public List getMessages(String queue) { + try { + BoolQueryBuilder query = boolQueryBuilder("queue='" + queue + "'", "*"); + + // Create the searchObjectIdsViaExpression source + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(query); + searchSourceBuilder.sort(new FieldSortBuilder("created").order(SortOrder.ASC)); + + // Generate the actual request to send to ES. + SearchRequest searchRequest = new SearchRequest(messageIndexPrefix + "*"); + searchRequest.source(searchSourceBuilder); + + SearchResponse response = + elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT); + return mapGetMessagesResponse(response); + } catch (Exception e) { + logger.error("Failed to get messages for queue: {}", queue, e); + } + return null; + } + + private List mapGetMessagesResponse(SearchResponse response) throws IOException { + SearchHit[] hits = response.getHits().getHits(); + TypeFactory factory = TypeFactory.defaultInstance(); + MapType type = factory.constructMapType(HashMap.class, String.class, String.class); + List messages = new ArrayList<>(hits.length); + for (SearchHit hit : hits) { + String source = hit.getSourceAsString(); + Map mapSource = objectMapper.readValue(source, type); + Message msg = new Message(mapSource.get("messageId"), mapSource.get("payload"), null); + messages.add(msg); + } + return messages; + } + + @Override + public List getEventExecutions(String event) { + try { + BoolQueryBuilder query = boolQueryBuilder("event='" + event + "'", "*"); + + // Create the searchObjectIdsViaExpression source + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(query); + searchSourceBuilder.sort(new FieldSortBuilder("created").order(SortOrder.ASC)); + + // Generate the actual request to send to ES. + SearchRequest searchRequest = new SearchRequest(eventIndexPrefix + "*"); + searchRequest.source(searchSourceBuilder); + + SearchResponse response = + elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT); + + return mapEventExecutionsResponse(response); + } catch (Exception e) { + logger.error("Failed to get executions for event: {}", event, e); + } + return null; + } + + private List mapEventExecutionsResponse(SearchResponse response) + throws IOException { + SearchHit[] hits = response.getHits().getHits(); + List executions = new ArrayList<>(hits.length); + for (SearchHit hit : hits) { + String source = hit.getSourceAsString(); + EventExecution tel = objectMapper.readValue(source, EventExecution.class); + executions.add(tel); + } + return executions; + } + + @Override + public void addMessage(String queue, Message message) { + try { + long startTime = Instant.now().toEpochMilli(); + Map doc = new HashMap<>(); + doc.put("messageId", message.getId()); + doc.put("payload", message.getPayload()); + doc.put("queue", queue); + doc.put("created", System.currentTimeMillis()); + + indexObject(messageIndexName, MSG_DOC_TYPE, doc); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for indexing message: {}", + endTime - startTime, + message.getId()); + Monitors.recordESIndexTime("add_message", MSG_DOC_TYPE, endTime - startTime); + } catch (Exception e) { + logger.error("Failed to index message: {}", message.getId(), e); + } + } + + @Override + public CompletableFuture asyncAddMessage(String queue, Message message) { + return CompletableFuture.runAsync(() -> addMessage(queue, message), executorService); + } + + @Override + public void addEventExecution(EventExecution eventExecution) { + try { + long startTime = Instant.now().toEpochMilli(); + String id = + eventExecution.getName() + + "." + + eventExecution.getEvent() + + "." + + eventExecution.getMessageId() + + "." + + eventExecution.getId(); + + indexObject(eventIndexName, EVENT_DOC_TYPE, id, eventExecution); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for indexing event execution: {}", + endTime - startTime, + eventExecution.getId()); + Monitors.recordESIndexTime("add_event_execution", EVENT_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "logQueue", ((ThreadPoolExecutor) logExecutorService).getQueue().size()); + } catch (Exception e) { + logger.error("Failed to index event execution: {}", eventExecution.getId(), e); + } + } + + @Override + public CompletableFuture asyncAddEventExecution(EventExecution eventExecution) { + return CompletableFuture.runAsync( + () -> addEventExecution(eventExecution), logExecutorService); + } + + @Override + public SearchResult searchWorkflows( + String query, String freeText, int start, int count, List sort) { + try { + return searchObjectIdsViaExpression( + query, start, count, sort, freeText, WORKFLOW_DOC_TYPE); + } catch (Exception e) { + throw new NonTransientException(e.getMessage(), e); + } + } + + @Override + public SearchResult searchWorkflowSummary( + String query, String freeText, int start, int count, List sort) { + try { + return searchObjectsViaExpression( + query, + start, + count, + sort, + freeText, + WORKFLOW_DOC_TYPE, + false, + WorkflowSummary.class); + } catch (Exception e) { + throw new TransientException(e.getMessage(), e); + } + } + + private SearchResult searchObjectsViaExpression( + String structuredQuery, + int start, + int size, + List sortOptions, + String freeTextQuery, + String docType, + boolean idOnly, + Class clazz) + throws ParserException, IOException { + QueryBuilder queryBuilder = boolQueryBuilder(structuredQuery, freeTextQuery); + return searchObjects( + getIndexName(docType), queryBuilder, start, size, sortOptions, idOnly, clazz); + } + + @Override + public SearchResult searchTasks( + String query, String freeText, int start, int count, List sort) { + try { + return searchObjectIdsViaExpression(query, start, count, sort, freeText, TASK_DOC_TYPE); + } catch (Exception e) { + throw new NonTransientException(e.getMessage(), e); + } + } + + @Override + public SearchResult searchTaskSummary( + String query, String freeText, int start, int count, List sort) { + try { + return searchObjectsViaExpression( + query, start, count, sort, freeText, TASK_DOC_TYPE, false, TaskSummary.class); + } catch (Exception e) { + throw new TransientException(e.getMessage(), e); + } + } + + @Override + public void removeWorkflow(String workflowId) { + long startTime = Instant.now().toEpochMilli(); + DeleteRequest request = new DeleteRequest(workflowIndexName, workflowId); + + try { + DeleteResponse response = elasticSearchClient.delete(request, RequestOptions.DEFAULT); + + if (response.getResult() == DocWriteResponse.Result.NOT_FOUND) { + logger.error("Index removal failed - document not found by id: {}", workflowId); + } + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for removing workflow: {}", endTime - startTime, workflowId); + Monitors.recordESIndexTime("remove_workflow", WORKFLOW_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (IOException e) { + logger.error("Failed to remove workflow {} from index", workflowId, e); + Monitors.error(className, "remove"); + } + } + + @Override + public CompletableFuture asyncRemoveWorkflow(String workflowId) { + return CompletableFuture.runAsync(() -> removeWorkflow(workflowId), executorService); + } + + @Override + public void updateWorkflow(String workflowInstanceId, String[] keys, Object[] values) { + try { + if (keys.length != values.length) { + throw new NonTransientException("Number of keys and values do not match"); + } + + long startTime = Instant.now().toEpochMilli(); + UpdateRequest request = new UpdateRequest(workflowIndexName, workflowInstanceId); + Map source = + IntStream.range(0, keys.length) + .boxed() + .collect(Collectors.toMap(i -> keys[i], i -> values[i])); + request.doc(source); + + logger.debug("Updating workflow {} with {}", workflowInstanceId, source); + elasticSearchClient.update(request, RequestOptions.DEFAULT); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for updating workflow: {}", + endTime - startTime, + workflowInstanceId); + Monitors.recordESIndexTime("update_workflow", WORKFLOW_DOC_TYPE, endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (Exception e) { + logger.error("Failed to update workflow {}", workflowInstanceId, e); + Monitors.error(className, "update"); + } + } + + @Override + public void removeTask(String workflowId, String taskId) { + long startTime = Instant.now().toEpochMilli(); + + SearchResult taskSearchResult = + searchTasks( + String.format("(taskId='%s') AND (workflowId='%s')", taskId, workflowId), + "*", + 0, + 1, + null); + + if (taskSearchResult.getTotalHits() == 0) { + logger.error("Task: {} does not belong to workflow: {}", taskId, workflowId); + Monitors.error(className, "removeTask"); + return; + } + + DeleteRequest request = new DeleteRequest(taskIndexName, taskId); + + try { + DeleteResponse response = elasticSearchClient.delete(request, RequestOptions.DEFAULT); + + if (response.getResult() != DocWriteResponse.Result.DELETED) { + logger.error("Index removal failed - task not found by id: {}", workflowId); + Monitors.error(className, "removeTask"); + return; + } + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for removing task:{} of workflow: {}", + endTime - startTime, + taskId, + workflowId); + Monitors.recordESIndexTime("remove_task", "", endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (IOException e) { + logger.error( + "Failed to remove task {} of workflow: {} from index", taskId, workflowId, e); + Monitors.error(className, "removeTask"); + } + } + + @Override + public CompletableFuture asyncRemoveTask(String workflowId, String taskId) { + return CompletableFuture.runAsync(() -> removeTask(workflowId, taskId), executorService); + } + + @Override + public void updateTask(String workflowId, String taskId, String[] keys, Object[] values) { + try { + if (keys.length != values.length) { + throw new IllegalArgumentException("Number of keys and values do not match"); + } + + long startTime = Instant.now().toEpochMilli(); + UpdateRequest request = new UpdateRequest(taskIndexName, taskId); + Map source = + IntStream.range(0, keys.length) + .boxed() + .collect(Collectors.toMap(i -> keys[i], i -> values[i])); + request.doc(source); + + logger.debug("Updating task: {} of workflow: {} with {}", taskId, workflowId, source); + elasticSearchClient.update(request, RequestOptions.DEFAULT); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for updating task: {} of workflow: {}", + endTime - startTime, + taskId, + workflowId); + Monitors.recordESIndexTime("update_task", "", endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + } catch (Exception e) { + logger.error("Failed to update task: {} of workflow: {}", taskId, workflowId, e); + Monitors.error(className, "update"); + } + } + + @Override + public CompletableFuture asyncUpdateTask( + String workflowId, String taskId, String[] keys, Object[] values) { + return CompletableFuture.runAsync( + () -> updateTask(workflowId, taskId, keys, values), executorService); + } + + @Override + public CompletableFuture asyncUpdateWorkflow( + String workflowInstanceId, String[] keys, Object[] values) { + return CompletableFuture.runAsync( + () -> updateWorkflow(workflowInstanceId, keys, values), executorService); + } + + @Override + public String get(String workflowInstanceId, String fieldToGet) { + GetRequest request = new GetRequest(workflowIndexName, workflowInstanceId); + GetResponse response; + try { + response = elasticSearchClient.get(request, RequestOptions.DEFAULT); + } catch (IOException e) { + logger.error( + "Unable to get Workflow: {} from ElasticSearch index: {}", + workflowInstanceId, + workflowIndexName, + e); + return null; + } + + if (response.isExists()) { + Map sourceAsMap = response.getSourceAsMap(); + if (sourceAsMap.get(fieldToGet) != null) { + return sourceAsMap.get(fieldToGet).toString(); + } + } + + logger.debug( + "Unable to find Workflow: {} in ElasticSearch index: {}.", + workflowInstanceId, + workflowIndexName); + return null; + } + + private SearchResult searchObjectIdsViaExpression( + String structuredQuery, + int start, + int size, + List sortOptions, + String freeTextQuery, + String docType) + throws ParserException, IOException { + QueryBuilder queryBuilder = boolQueryBuilder(structuredQuery, freeTextQuery); + return searchObjectIds(getIndexName(docType), queryBuilder, start, size, sortOptions); + } + + private SearchResult searchObjectIdsViaExpression( + String structuredQuery, + int start, + int size, + List sortOptions, + String freeTextQuery, + String docType, + Class clazz) + throws ParserException, IOException { + QueryBuilder queryBuilder = boolQueryBuilder(structuredQuery, freeTextQuery); + return searchObjects( + getIndexName(docType), queryBuilder, start, size, sortOptions, false, clazz); + } + + private SearchResult searchObjectIds( + String indexName, QueryBuilder queryBuilder, int start, int size) throws IOException { + return searchObjectIds(indexName, queryBuilder, start, size, null); + } + + /** + * Tries to find object ids for a given query in an index. + * + * @param indexName The name of the index. + * @param queryBuilder The query to use for searching. + * @param start The start to use. + * @param size The total return size. + * @param sortOptions A list of string options to sort in the form VALUE:ORDER; where ORDER is + * optional and can be either ASC OR DESC. + * @return The SearchResults which includes the count and IDs that were found. + * @throws IOException If we cannot communicate with ES. + */ + private SearchResult searchObjectIds( + String indexName, + QueryBuilder queryBuilder, + int start, + int size, + List sortOptions) + throws IOException { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(queryBuilder); + searchSourceBuilder.from(start); + searchSourceBuilder.size(size); + + if (sortOptions != null && !sortOptions.isEmpty()) { + + for (String sortOption : sortOptions) { + SortOrder order = SortOrder.ASC; + String field = sortOption; + int index = sortOption.indexOf(":"); + if (index > 0) { + field = sortOption.substring(0, index); + order = SortOrder.valueOf(sortOption.substring(index + 1)); + } + searchSourceBuilder.sort(new FieldSortBuilder(field).order(order)); + } + } + + // Generate the actual request to send to ES. + SearchRequest searchRequest = new SearchRequest(indexName); + searchRequest.source(searchSourceBuilder); + + SearchResponse response = elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT); + + List result = new LinkedList<>(); + response.getHits().forEach(hit -> result.add(hit.getId())); + long count = response.getHits().getTotalHits().value; + return new SearchResult<>(count, result); + } + + private SearchResult searchObjects( + String indexName, + QueryBuilder queryBuilder, + int start, + int size, + List sortOptions, + boolean idOnly, + Class clazz) + throws IOException { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(queryBuilder); + searchSourceBuilder.from(start); + searchSourceBuilder.size(size); + if (idOnly) { + searchSourceBuilder.fetchSource(false); + } + + if (sortOptions != null && !sortOptions.isEmpty()) { + + for (String sortOption : sortOptions) { + SortOrder order = SortOrder.ASC; + String field = sortOption; + int index = sortOption.indexOf(":"); + if (index > 0) { + field = sortOption.substring(0, index); + order = SortOrder.valueOf(sortOption.substring(index + 1)); + } + searchSourceBuilder.sort(new FieldSortBuilder(field).order(order)); + } + } + + // Generate the actual request to send to ES. + SearchRequest searchRequest = new SearchRequest(indexName); + searchRequest.source(searchSourceBuilder); + + SearchResponse response = elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT); + return mapSearchResult(response, idOnly, clazz); + } + + private SearchResult mapSearchResult( + SearchResponse response, boolean idOnly, Class clazz) { + SearchHits searchHits = response.getHits(); + long count = searchHits.getTotalHits().value; + List result; + if (idOnly) { + result = + Arrays.stream(searchHits.getHits()) + .map(hit -> clazz.cast(hit.getId())) + .collect(Collectors.toList()); + } else { + result = + Arrays.stream(searchHits.getHits()) + .map( + hit -> { + try { + return objectMapper.readValue( + hit.getSourceAsString(), clazz); + } catch (JsonProcessingException e) { + logger.error( + "Failed to de-serialize elasticsearch from source: {}", + hit.getSourceAsString(), + e); + } + return null; + }) + .collect(Collectors.toList()); + } + return new SearchResult<>(count, result); + } + + @Override + public List searchArchivableWorkflows(String indexName, long archiveTtlDays) { + QueryBuilder q = + QueryBuilders.boolQuery() + .must( + QueryBuilders.rangeQuery("endTime") + .lt(LocalDate.now().minusDays(archiveTtlDays).toString()) + .gte( + LocalDate.now() + .minusDays(archiveTtlDays) + .minusDays(1) + .toString())) + .should(QueryBuilders.termQuery("status", "COMPLETED")) + .should(QueryBuilders.termQuery("status", "FAILED")) + .should(QueryBuilders.termQuery("status", "TIMED_OUT")) + .should(QueryBuilders.termQuery("status", "TERMINATED")) + .mustNot(QueryBuilders.existsQuery("archived")) + .minimumShouldMatch(1); + + SearchResult workflowIds; + try { + workflowIds = searchObjectIds(indexName, q, 0, 1000); + } catch (IOException e) { + logger.error("Unable to communicate with ES to find archivable workflows", e); + return Collections.emptyList(); + } + + return workflowIds.getResults(); + } + + @Override + public long getWorkflowCount(String query, String freeText) { + try { + return getObjectCounts(query, freeText, WORKFLOW_DOC_TYPE); + } catch (Exception e) { + throw new NonTransientException(e.getMessage(), e); + } + } + + private long getObjectCounts(String structuredQuery, String freeTextQuery, String docType) + throws ParserException, IOException { + QueryBuilder queryBuilder = boolQueryBuilder(structuredQuery, freeTextQuery); + + String indexName = getIndexName(docType); + CountRequest countRequest = new CountRequest(new String[] {indexName}, queryBuilder); + CountResponse countResponse = + elasticSearchClient.count(countRequest, RequestOptions.DEFAULT); + return countResponse.getCount(); + } + + public List searchRecentRunningWorkflows( + int lastModifiedHoursAgoFrom, int lastModifiedHoursAgoTo) { + DateTime dateTime = new DateTime(); + QueryBuilder q = + QueryBuilders.boolQuery() + .must( + QueryBuilders.rangeQuery("updateTime") + .gt(dateTime.minusHours(lastModifiedHoursAgoFrom))) + .must( + QueryBuilders.rangeQuery("updateTime") + .lt(dateTime.minusHours(lastModifiedHoursAgoTo))) + .must(QueryBuilders.termQuery("status", "RUNNING")); + + SearchResult workflowIds; + try { + workflowIds = + searchObjectIds( + workflowIndexName, + q, + 0, + 5000, + Collections.singletonList("updateTime:ASC")); + } catch (IOException e) { + logger.error("Unable to communicate with ES to find recent running workflows", e); + return Collections.emptyList(); + } + + return workflowIds.getResults(); + } + + private void indexObject(final String index, final String docType, final Object doc) { + indexObject(index, docType, null, doc); + } + + private void indexObject( + final String index, final String docType, final String docId, final Object doc) { + + byte[] docBytes; + try { + docBytes = objectMapper.writeValueAsBytes(doc); + } catch (JsonProcessingException e) { + logger.error("Failed to convert {} '{}' to byte string", docType, docId); + return; + } + IndexRequest request = new IndexRequest(index); + request.id(docId).source(docBytes, XContentType.JSON); + + if (bulkRequests.get(docType) == null) { + bulkRequests.put( + docType, new BulkRequests(System.currentTimeMillis(), new BulkRequest())); + } + + bulkRequests.get(docType).getBulkRequest().add(request); + if (bulkRequests.get(docType).getBulkRequest().numberOfActions() >= this.indexBatchSize) { + indexBulkRequest(docType); + } + } + + private synchronized void indexBulkRequest(String docType) { + if (bulkRequests.get(docType).getBulkRequest() != null + && bulkRequests.get(docType).getBulkRequest().numberOfActions() > 0) { + synchronized (bulkRequests.get(docType).getBulkRequest()) { + indexWithRetry( + bulkRequests.get(docType).getBulkRequest().get(), + "Bulk Indexing " + docType, + docType); + bulkRequests.put( + docType, new BulkRequests(System.currentTimeMillis(), new BulkRequest())); + } + } + } + + /** + * Performs an index operation with a retry. + * + * @param request The index request that we want to perform. + * @param operationDescription The type of operation that we are performing. + */ + private void indexWithRetry( + final BulkRequest request, final String operationDescription, String docType) { + try { + long startTime = Instant.now().toEpochMilli(); + retryTemplate.execute( + context -> elasticSearchClient.bulk(request, RequestOptions.DEFAULT)); + long endTime = Instant.now().toEpochMilli(); + logger.debug( + "Time taken {} for indexing object of type: {}", endTime - startTime, docType); + Monitors.recordESIndexTime("index_object", docType, endTime - startTime); + Monitors.recordWorkerQueueSize( + "indexQueue", ((ThreadPoolExecutor) executorService).getQueue().size()); + Monitors.recordWorkerQueueSize( + "logQueue", ((ThreadPoolExecutor) logExecutorService).getQueue().size()); + } catch (Exception e) { + Monitors.error(className, "index"); + logger.error("Failed to index {} for request type: {}", request, docType, e); + } + } + + /** + * Flush the buffers if bulk requests have not been indexed for the past {@link + * ElasticSearchProperties#getAsyncBufferFlushTimeout()} seconds This is to prevent data loss in + * case the instance is terminated, while the buffer still holds documents to be indexed. + */ + private void flushBulkRequests() { + bulkRequests.entrySet().stream() + .filter( + entry -> + (System.currentTimeMillis() - entry.getValue().getLastFlushTime()) + >= asyncBufferFlushTimeout * 1000L) + .filter( + entry -> + entry.getValue().getBulkRequest() != null + && entry.getValue().getBulkRequest().numberOfActions() > 0) + .forEach( + entry -> { + logger.debug( + "Flushing bulk request buffer for type {}, size: {}", + entry.getKey(), + entry.getValue().getBulkRequest().numberOfActions()); + indexBulkRequest(entry.getKey()); + }); + } + + private static class BulkRequests { + + private final long lastFlushTime; + private final BulkRequestWrapper bulkRequest; + + long getLastFlushTime() { + return lastFlushTime; + } + + BulkRequestWrapper getBulkRequest() { + return bulkRequest; + } + + BulkRequests(long lastFlushTime, BulkRequest bulkRequest) { + this.lastFlushTime = lastFlushTime; + this.bulkRequest = new BulkRequestWrapper(bulkRequest); + } + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java new file mode 100644 index 000000000..7cb3d7f3b --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java @@ -0,0 +1,118 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; + +import com.netflix.conductor.es7.dao.query.parser.internal.AbstractNode; +import com.netflix.conductor.es7.dao.query.parser.internal.BooleanOp; +import com.netflix.conductor.es7.dao.query.parser.internal.ParserException; + +/** + * @author Viren + */ +public class Expression extends AbstractNode implements FilterProvider { + + private NameValue nameVal; + + private GroupedExpression ge; + + private BooleanOp op; + + private Expression rhs; + + public Expression(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] peeked = peek(1); + + if (peeked[0] == '(') { + this.ge = new GroupedExpression(is); + } else { + this.nameVal = new NameValue(is); + } + + peeked = peek(3); + if (isBoolOpr(peeked)) { + // we have an expression next + this.op = new BooleanOp(is); + this.rhs = new Expression(is); + } + } + + public boolean isBinaryExpr() { + return this.op != null; + } + + public BooleanOp getOperator() { + return this.op; + } + + public Expression getRightHandSide() { + return this.rhs; + } + + public boolean isNameValue() { + return this.nameVal != null; + } + + public NameValue getNameValue() { + return this.nameVal; + } + + public GroupedExpression getGroupedExpression() { + return this.ge; + } + + @Override + public QueryBuilder getFilterBuilder() { + QueryBuilder lhs = null; + if (nameVal != null) { + lhs = nameVal.getFilterBuilder(); + } else { + lhs = ge.getFilterBuilder(); + } + + if (this.isBinaryExpr()) { + QueryBuilder rhsFilter = rhs.getFilterBuilder(); + if (this.op.isAnd()) { + return QueryBuilders.boolQuery().must(lhs).must(rhsFilter); + } else { + return QueryBuilders.boolQuery().should(lhs).should(rhsFilter); + } + } else { + return lhs; + } + } + + @Override + public String toString() { + if (isBinaryExpr()) { + return "" + (nameVal == null ? ge : nameVal) + op + rhs; + } else { + return "" + (nameVal == null ? ge : nameVal); + } + } + + public static Expression fromString(String value) throws ParserException { + return new Expression(new BufferedInputStream(new ByteArrayInputStream(value.getBytes()))); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java new file mode 100644 index 000000000..f4de4d36b --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java @@ -0,0 +1,26 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import org.elasticsearch.index.query.QueryBuilder; + +/** + * @author Viren + */ +public interface FilterProvider { + + /** + * @return FilterBuilder for elasticsearch + */ + public QueryBuilder getFilterBuilder(); +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java new file mode 100644 index 000000000..67aa8965c --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java @@ -0,0 +1,60 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import java.io.InputStream; + +import org.elasticsearch.index.query.QueryBuilder; + +import com.netflix.conductor.es7.dao.query.parser.internal.AbstractNode; +import com.netflix.conductor.es7.dao.query.parser.internal.ParserException; + +/** + * @author Viren + */ +public class GroupedExpression extends AbstractNode implements FilterProvider { + + private Expression expression; + + public GroupedExpression(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] peeked = read(1); + assertExpected(peeked, "("); + + this.expression = new Expression(is); + + peeked = read(1); + assertExpected(peeked, ")"); + } + + @Override + public String toString() { + return "(" + expression + ")"; + } + + /** + * @return the expression + */ + public Expression getExpression() { + return expression; + } + + @Override + public QueryBuilder getFilterBuilder() { + return expression.getFilterBuilder(); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java new file mode 100644 index 000000000..b81c9f94b --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import java.io.InputStream; + +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; + +import com.netflix.conductor.es7.dao.query.parser.internal.AbstractNode; +import com.netflix.conductor.es7.dao.query.parser.internal.ComparisonOp; +import com.netflix.conductor.es7.dao.query.parser.internal.ComparisonOp.Operators; +import com.netflix.conductor.es7.dao.query.parser.internal.ConstValue; +import com.netflix.conductor.es7.dao.query.parser.internal.ListConst; +import com.netflix.conductor.es7.dao.query.parser.internal.Name; +import com.netflix.conductor.es7.dao.query.parser.internal.ParserException; +import com.netflix.conductor.es7.dao.query.parser.internal.Range; + +/** + * @author Viren + *

+ * Represents an expression of the form as below:
+ * key OPR value
+ * OPR is the comparison operator which could be on the following:
+ * 	>, <, = , !=, IN, BETWEEN
+ * 
+ */ +public class NameValue extends AbstractNode implements FilterProvider { + + private Name name; + + private ComparisonOp op; + + private ConstValue value; + + private Range range; + + private ListConst valueList; + + public NameValue(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + this.name = new Name(is); + this.op = new ComparisonOp(is); + + if (this.op.getOperator().equals(Operators.BETWEEN.value())) { + this.range = new Range(is); + } + if (this.op.getOperator().equals(Operators.IN.value())) { + this.valueList = new ListConst(is); + } else { + this.value = new ConstValue(is); + } + } + + @Override + public String toString() { + return "" + name + op + value; + } + + /** + * @return the name + */ + public Name getName() { + return name; + } + + /** + * @return the op + */ + public ComparisonOp getOp() { + return op; + } + + /** + * @return the value + */ + public ConstValue getValue() { + return value; + } + + @Override + public QueryBuilder getFilterBuilder() { + if (op.getOperator().equals(Operators.EQUALS.value())) { + return QueryBuilders.queryStringQuery( + name.getName() + ":" + value.getValue().toString()); + } else if (op.getOperator().equals(Operators.BETWEEN.value())) { + return QueryBuilders.rangeQuery(name.getName()) + .from(range.getLow()) + .to(range.getHigh()); + } else if (op.getOperator().equals(Operators.IN.value())) { + return QueryBuilders.termsQuery(name.getName(), valueList.getList()); + } else if (op.getOperator().equals(Operators.NOT_EQUALS.value())) { + return QueryBuilders.queryStringQuery( + "NOT " + name.getName() + ":" + value.getValue().toString()); + } else if (op.getOperator().equals(Operators.GREATER_THAN.value())) { + return QueryBuilders.rangeQuery(name.getName()) + .from(value.getValue()) + .includeLower(false) + .includeUpper(false); + } else if (op.getOperator().equals(Operators.IS.value())) { + if (value.getSysConstant().equals(ConstValue.SystemConsts.NULL)) { + return QueryBuilders.boolQuery() + .mustNot( + QueryBuilders.boolQuery() + .must(QueryBuilders.matchAllQuery()) + .mustNot(QueryBuilders.existsQuery(name.getName()))); + } else if (value.getSysConstant().equals(ConstValue.SystemConsts.NOT_NULL)) { + return QueryBuilders.boolQuery() + .mustNot( + QueryBuilders.boolQuery() + .must(QueryBuilders.matchAllQuery()) + .must(QueryBuilders.existsQuery(name.getName()))); + } + } else if (op.getOperator().equals(Operators.LESS_THAN.value())) { + return QueryBuilders.rangeQuery(name.getName()) + .to(value.getValue()) + .includeLower(false) + .includeUpper(false); + } else if (op.getOperator().equals(Operators.STARTS_WITH.value())) { + return QueryBuilders.prefixQuery(name.getName(), value.getUnquotedValue()); + } + + throw new IllegalStateException("Incorrect/unsupported operators"); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java new file mode 100644 index 000000000..bfbd3988f --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java @@ -0,0 +1,178 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author Viren + */ +public abstract class AbstractNode { + + public static final Pattern WHITESPACE = Pattern.compile("\\s"); + + protected static Set comparisonOprs = new HashSet(); + + static { + comparisonOprs.add('>'); + comparisonOprs.add('<'); + comparisonOprs.add('='); + } + + protected InputStream is; + + protected AbstractNode(InputStream is) throws ParserException { + this.is = is; + this.parse(); + } + + protected boolean isNumber(String test) { + try { + // If you can convert to a big decimal value, then it is a number. + new BigDecimal(test); + return true; + + } catch (NumberFormatException e) { + // Ignore + } + return false; + } + + protected boolean isBoolOpr(byte[] buffer) { + if (buffer.length > 1 && buffer[0] == 'O' && buffer[1] == 'R') { + return true; + } else if (buffer.length > 2 && buffer[0] == 'A' && buffer[1] == 'N' && buffer[2] == 'D') { + return true; + } + return false; + } + + protected boolean isComparisonOpr(byte[] buffer) { + if (buffer[0] == 'I' && buffer[1] == 'N') { + return true; + } else if (buffer[0] == '!' && buffer[1] == '=') { + return true; + } else { + return comparisonOprs.contains((char) buffer[0]); + } + } + + protected byte[] peek(int length) throws Exception { + return read(length, true); + } + + protected byte[] read(int length) throws Exception { + return read(length, false); + } + + protected String readToken() throws Exception { + skipWhitespace(); + StringBuilder sb = new StringBuilder(); + while (is.available() > 0) { + char c = (char) peek(1)[0]; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + is.skip(1); + break; + } else if (c == '=' || c == '>' || c == '<' || c == '!') { + // do not skip + break; + } + sb.append(c); + is.skip(1); + } + return sb.toString().trim(); + } + + protected boolean isNumeric(char c) { + if (c == '-' || c == 'e' || (c >= '0' && c <= '9') || c == '.') { + return true; + } + return false; + } + + protected void assertExpected(byte[] found, String expected) throws ParserException { + assertExpected(new String(found), expected); + } + + protected void assertExpected(String found, String expected) throws ParserException { + if (!found.equals(expected)) { + throw new ParserException("Expected " + expected + ", found " + found); + } + } + + protected void assertExpected(char found, char expected) throws ParserException { + if (found != expected) { + throw new ParserException("Expected " + expected + ", found " + found); + } + } + + protected static void efor(int length, FunctionThrowingException consumer) + throws Exception { + for (int i = 0; i < length; i++) { + consumer.accept(i); + } + } + + protected abstract void _parse() throws Exception; + + // Public stuff here + private void parse() throws ParserException { + // skip white spaces + skipWhitespace(); + try { + _parse(); + } catch (Exception e) { + System.out.println("\t" + this.getClass().getSimpleName() + "->" + this.toString()); + if (!(e instanceof ParserException)) { + throw new ParserException("Error parsing", e); + } else { + throw (ParserException) e; + } + } + skipWhitespace(); + } + + // Private methods + + private byte[] read(int length, boolean peekOnly) throws Exception { + byte[] buf = new byte[length]; + if (peekOnly) { + is.mark(length); + } + efor(length, (Integer c) -> buf[c] = (byte) is.read()); + if (peekOnly) { + is.reset(); + } + return buf; + } + + protected void skipWhitespace() throws ParserException { + try { + while (is.available() > 0) { + byte c = peek(1)[0]; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + // skip + read(1); + } else { + break; + } + } + } catch (Exception e) { + throw new ParserException(e.getMessage(), e); + } + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java new file mode 100644 index 000000000..f37c5f200 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; + +/** + * @author Viren + */ +public class BooleanOp extends AbstractNode { + + private String value; + + public BooleanOp(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] buffer = peek(3); + if (buffer.length > 1 && buffer[0] == 'O' && buffer[1] == 'R') { + this.value = "OR"; + } else if (buffer.length > 2 && buffer[0] == 'A' && buffer[1] == 'N' && buffer[2] == 'D') { + this.value = "AND"; + } else { + throw new ParserException("No valid boolean operator found..."); + } + read(this.value.length()); + } + + @Override + public String toString() { + return " " + value + " "; + } + + public String getOperator() { + return value; + } + + public boolean isAnd() { + return "AND".equals(value); + } + + public boolean isOr() { + return "OR".equals(value); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java new file mode 100644 index 000000000..55d8d49a8 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; + +/** + * @author Viren + */ +public class ComparisonOp extends AbstractNode { + + public enum Operators { + BETWEEN("BETWEEN"), + EQUALS("="), + LESS_THAN("<"), + GREATER_THAN(">"), + IN("IN"), + NOT_EQUALS("!="), + IS("IS"), + STARTS_WITH("STARTS_WITH"); + + private final String value; + + Operators(String value) { + this.value = value; + } + + public String value() { + return value; + } + } + + static { + int max = 0; + for (Operators op : Operators.values()) { + max = Math.max(max, op.value().length()); + } + maxOperatorLength = max; + } + + private static final int maxOperatorLength; + + private static final int betweenLen = Operators.BETWEEN.value().length(); + private static final int startsWithLen = Operators.STARTS_WITH.value().length(); + + private String value; + + public ComparisonOp(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] peeked = peek(maxOperatorLength); + if (peeked[0] == '=' || peeked[0] == '>' || peeked[0] == '<') { + this.value = new String(peeked, 0, 1); + } else if (peeked[0] == 'I' && peeked[1] == 'N') { + this.value = "IN"; + } else if (peeked[0] == 'I' && peeked[1] == 'S') { + this.value = "IS"; + } else if (peeked[0] == '!' && peeked[1] == '=') { + this.value = "!="; + } else if (peeked.length >= betweenLen + && peeked[0] == 'B' + && peeked[1] == 'E' + && peeked[2] == 'T' + && peeked[3] == 'W' + && peeked[4] == 'E' + && peeked[5] == 'E' + && peeked[6] == 'N') { + this.value = Operators.BETWEEN.value(); + } else if (peeked.length == startsWithLen + && new String(peeked).equals(Operators.STARTS_WITH.value())) { + this.value = Operators.STARTS_WITH.value(); + } else { + throw new ParserException( + "Expecting an operator (=, >, <, !=, BETWEEN, IN, STARTS_WITH), but found none. Peeked=>" + + new String(peeked)); + } + + read(this.value.length()); + } + + @Override + public String toString() { + return " " + value + " "; + } + + public String getOperator() { + return value; + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java new file mode 100644 index 000000000..edc9513c1 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; + +/** + * @author Viren Constant value can be: + *

    + *
  1. List of values (a,b,c) + *
  2. Range of values (m AND n) + *
  3. A value (x) + *
  4. A value is either a string or a number + *
+ */ +public class ConstValue extends AbstractNode { + + public static enum SystemConsts { + NULL("null"), + NOT_NULL("not null"); + private String value; + + SystemConsts(String value) { + this.value = value; + } + + public String value() { + return value; + } + } + + private static String QUOTE = "\""; + + private Object value; + + private SystemConsts sysConsts; + + public ConstValue(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] peeked = peek(4); + String sp = new String(peeked).trim(); + // Read a constant value (number or a string) + if (peeked[0] == '"' || peeked[0] == '\'') { + this.value = readString(is); + } else if (sp.toLowerCase().startsWith("not")) { + this.value = SystemConsts.NOT_NULL.value(); + sysConsts = SystemConsts.NOT_NULL; + read(SystemConsts.NOT_NULL.value().length()); + } else if (sp.equalsIgnoreCase(SystemConsts.NULL.value())) { + this.value = SystemConsts.NULL.value(); + sysConsts = SystemConsts.NULL; + read(SystemConsts.NULL.value().length()); + } else { + this.value = readNumber(is); + } + } + + private String readNumber(InputStream is) throws Exception { + StringBuilder sb = new StringBuilder(); + while (is.available() > 0) { + is.mark(1); + char c = (char) is.read(); + if (!isNumeric(c)) { + is.reset(); + break; + } else { + sb.append(c); + } + } + String numValue = sb.toString().trim(); + return numValue; + } + + /** + * Reads an escaped string + * + * @throws Exception + */ + private String readString(InputStream is) throws Exception { + char delim = (char) read(1)[0]; + StringBuilder sb = new StringBuilder(); + boolean valid = false; + while (is.available() > 0) { + char c = (char) is.read(); + if (c == delim) { + valid = true; + break; + } else if (c == '\\') { + // read the next character as part of the value + c = (char) is.read(); + sb.append(c); + } else { + sb.append(c); + } + } + if (!valid) { + throw new ParserException( + "String constant is not quoted with <" + delim + "> : " + sb.toString()); + } + return QUOTE + sb.toString() + QUOTE; + } + + public Object getValue() { + return value; + } + + @Override + public String toString() { + return "" + value; + } + + public String getUnquotedValue() { + String result = toString(); + if (result.length() >= 2 && result.startsWith(QUOTE) && result.endsWith(QUOTE)) { + result = result.substring(1, result.length() - 1); + } + return result; + } + + public boolean isSysConstant() { + return this.sysConsts != null; + } + + public SystemConsts getSysConstant() { + return this.sysConsts; + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java new file mode 100644 index 000000000..97afc3684 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +/** + * @author Viren + */ +@FunctionalInterface +public interface FunctionThrowingException { + + void accept(T t) throws Exception; +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java new file mode 100644 index 000000000..87227a863 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Viren List of constants + */ +public class ListConst extends AbstractNode { + + private List values; + + public ListConst(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + byte[] peeked = read(1); + assertExpected(peeked, "("); + this.values = readList(); + } + + private List readList() throws Exception { + List list = new LinkedList(); + boolean valid = false; + char c; + + StringBuilder sb = new StringBuilder(); + while (is.available() > 0) { + c = (char) is.read(); + if (c == ')') { + valid = true; + break; + } else if (c == ',') { + list.add(sb.toString().trim()); + sb = new StringBuilder(); + } else { + sb.append(c); + } + } + list.add(sb.toString().trim()); + if (!valid) { + throw new ParserException("Expected ')' but never encountered in the stream"); + } + return list; + } + + public List getList() { + return (List) values; + } + + @Override + public String toString() { + return values.toString(); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java new file mode 100644 index 000000000..589b62ccd --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; + +/** + * @author Viren Represents the name of the field to be searched against. + */ +public class Name extends AbstractNode { + + private String value; + + public Name(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + this.value = readToken(); + } + + @Override + public String toString() { + return value; + } + + public String getName() { + return value; + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java new file mode 100644 index 000000000..08a9d7b08 --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +/** + * @author Viren + */ +@SuppressWarnings("serial") +public class ParserException extends Exception { + + public ParserException(String message) { + super(message); + } + + public ParserException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java new file mode 100644 index 000000000..6315e00fa --- /dev/null +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.InputStream; + +/** + * @author Viren + */ +public class Range extends AbstractNode { + + private String low; + + private String high; + + public Range(InputStream is) throws ParserException { + super(is); + } + + @Override + protected void _parse() throws Exception { + this.low = readNumber(is); + + skipWhitespace(); + byte[] peeked = read(3); + assertExpected(peeked, "AND"); + skipWhitespace(); + + String num = readNumber(is); + if (num == null || "".equals(num)) { + throw new ParserException("Missing the upper range value..."); + } + this.high = num; + } + + private String readNumber(InputStream is) throws Exception { + StringBuilder sb = new StringBuilder(); + while (is.available() > 0) { + is.mark(1); + char c = (char) is.read(); + if (!isNumeric(c)) { + is.reset(); + break; + } else { + sb.append(c); + } + } + String numValue = sb.toString().trim(); + return numValue; + } + + /** + * @return the low + */ + public String getLow() { + return low; + } + + /** + * @return the high + */ + public String getHigh() { + return high; + } + + @Override + public String toString() { + return low + " AND " + high; + } +} diff --git a/es7-persistence/src/main/resources/mappings_docType_task.json b/es7-persistence/src/main/resources/mappings_docType_task.json new file mode 100644 index 000000000..3d102a013 --- /dev/null +++ b/es7-persistence/src/main/resources/mappings_docType_task.json @@ -0,0 +1,66 @@ +{ + "properties": { + "correlationId": { + "type": "keyword", + "index": true + }, + "endTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "executionTime": { + "type": "long" + }, + "input": { + "type": "text", + "index": true + }, + "output": { + "type": "text", + "index": true + }, + "queueWaitTime": { + "type": "long" + }, + "reasonForIncompletion": { + "type": "keyword", + "index": true + }, + "scheduledTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "startTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "status": { + "type": "keyword", + "index": true + }, + "taskDefName": { + "type": "keyword", + "index": true + }, + "taskId": { + "type": "keyword", + "index": true + }, + "taskType": { + "type": "keyword", + "index": true + }, + "updateTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis" + }, + "workflowId": { + "type": "keyword", + "index": true + }, + "workflowType": { + "type": "keyword", + "index": true + } + } +} diff --git a/es7-persistence/src/main/resources/mappings_docType_workflow.json b/es7-persistence/src/main/resources/mappings_docType_workflow.json new file mode 100644 index 000000000..51adac631 --- /dev/null +++ b/es7-persistence/src/main/resources/mappings_docType_workflow.json @@ -0,0 +1,72 @@ +{ + "properties": { + "correlationId": { + "type": "keyword", + "index": true, + "doc_values": true + }, + "endTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis", + "doc_values": true + }, + "executionTime": { + "type": "long", + "doc_values": true + }, + "failedReferenceTaskNames": { + "type": "text", + "index": false + }, + "input": { + "type": "text", + "index": true + }, + "output": { + "type": "text", + "index": true + }, + "reasonForIncompletion": { + "type": "keyword", + "index": true, + "doc_values": true + }, + "startTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis", + "doc_values": true + }, + "status": { + "type": "keyword", + "index": true, + "doc_values": true + }, + "updateTime": { + "type": "date", + "format": "strict_date_optional_time||epoch_millis", + "doc_values": true + }, + "version": { + "type": "long", + "doc_values": true + }, + "workflowId": { + "type": "keyword", + "index": true, + "doc_values": true + }, + "workflowType": { + "type": "keyword", + "index": true, + "doc_values": true + }, + "rawJSON": { + "type": "text", + "index": false + }, + "event": { + "type": "keyword", + "index": true + } + } +} diff --git a/es7-persistence/src/main/resources/template_event.json b/es7-persistence/src/main/resources/template_event.json new file mode 100644 index 000000000..3a0150320 --- /dev/null +++ b/es7-persistence/src/main/resources/template_event.json @@ -0,0 +1,48 @@ +{ + "index_patterns": [ "*event*" ], + "template": { + "settings": { + "refresh_interval": "1s" + }, + "mappings": { + "properties": { + "action": { + "type": "keyword", + "index": true + }, + "created": { + "type": "long" + }, + "event": { + "type": "keyword", + "index": true + }, + "id": { + "type": "keyword", + "index": true + }, + "messageId": { + "type": "keyword", + "index": true + }, + "name": { + "type": "keyword", + "index": true + }, + "output": { + "properties": { + "workflowId": { + "type": "keyword", + "index": true + } + } + }, + "status": { + "type": "keyword", + "index": true + } + } + }, + "aliases" : { } + } +} diff --git a/es7-persistence/src/main/resources/template_message.json b/es7-persistence/src/main/resources/template_message.json new file mode 100644 index 000000000..63d571aea --- /dev/null +++ b/es7-persistence/src/main/resources/template_message.json @@ -0,0 +1,28 @@ +{ + "index_patterns": [ "*message*" ], + "template": { + "settings": { + "refresh_interval": "1s" + }, + "mappings": { + "properties": { + "created": { + "type": "long" + }, + "messageId": { + "type": "keyword", + "index": true + }, + "payload": { + "type": "keyword", + "index": true + }, + "queue": { + "type": "keyword", + "index": true + } + } + }, + "aliases": { } + } +} diff --git a/es7-persistence/src/main/resources/template_task_log.json b/es7-persistence/src/main/resources/template_task_log.json new file mode 100644 index 000000000..f7ec4bff0 --- /dev/null +++ b/es7-persistence/src/main/resources/template_task_log.json @@ -0,0 +1,24 @@ +{ + "index_patterns": [ "*task*log*" ], + "template": { + "settings": { + "refresh_interval": "1s" + }, + "mappings": { + "properties": { + "createdTime": { + "type": "long" + }, + "log": { + "type": "keyword", + "index": true + }, + "taskId": { + "type": "keyword", + "index": true + } + } + }, + "aliases": { } + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java new file mode 100644 index 000000000..a209c3ea7 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.junit.After; +import org.junit.Before; +import org.springframework.retry.support.RetryTemplate; + +public abstract class ElasticSearchRestDaoBaseTest extends ElasticSearchTest { + + protected RestClient restClient; + protected ElasticSearchRestDAOV7 indexDAO; + + @Before + public void setup() throws Exception { + String httpHostAddress = container.getHttpHostAddress(); + String host = httpHostAddress.split(":")[0]; + int port = Integer.parseInt(httpHostAddress.split(":")[1]); + + properties.setUrl("http://" + httpHostAddress); + + RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(host, port, "http")); + restClient = restClientBuilder.build(); + + indexDAO = + new ElasticSearchRestDAOV7( + restClientBuilder, new RetryTemplate(), properties, objectMapper); + indexDAO.setup(); + } + + @After + public void tearDown() throws Exception { + deleteAllIndices(); + + if (restClient != null) { + restClient.close(); + } + } + + private void deleteAllIndices() throws IOException { + Response beforeResponse = restClient.performRequest(new Request("GET", "/_cat/indices")); + + Reader streamReader = new InputStreamReader(beforeResponse.getEntity().getContent()); + BufferedReader bufferedReader = new BufferedReader(streamReader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + String[] fields = line.split("\\s"); + String endpoint = String.format("/%s", fields[2]); + + restClient.performRequest(new Request("DELETE", endpoint)); + } + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java new file mode 100644 index 000000000..4004be56f --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.es7.config.ElasticSearchProperties; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@ContextConfiguration( + classes = {TestObjectMapperConfiguration.class, ElasticSearchTest.TestConfiguration.class}) +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = {"conductor.indexing.enabled=true", "conductor.elasticsearch.version=7"}) +public abstract class ElasticSearchTest { + + @Configuration + static class TestConfiguration { + + @Bean + public ElasticSearchProperties elasticSearchProperties() { + return new ElasticSearchProperties(); + } + } + + protected static final ElasticsearchContainer container = + new ElasticsearchContainer( + DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag("7.10.2")); // this should match the client version + + @Autowired protected ObjectMapper objectMapper; + + @Autowired protected ElasticSearchProperties properties; + + @BeforeClass + public static void startServer() { + container.start(); + } + + @AfterClass + public static void stopServer() { + container.stop(); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java new file mode 100644 index 000000000..f63110039 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.update.UpdateRequest; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestBulkRequestBuilderWrapper { + BulkRequestBuilder builder = Mockito.mock(BulkRequestBuilder.class); + BulkRequestBuilderWrapper wrapper = new BulkRequestBuilderWrapper(builder); + + @Test(expected = Exception.class) + public void testAddNullUpdateRequest() { + wrapper.add((UpdateRequest) null); + } + + @Test(expected = Exception.class) + public void testAddNullIndexRequest() { + wrapper.add((IndexRequest) null); + } + + @Test + public void testBuilderCalls() { + IndexRequest indexRequest = new IndexRequest(); + UpdateRequest updateRequest = new UpdateRequest(); + + wrapper.add(indexRequest); + wrapper.add(updateRequest); + wrapper.numberOfActions(); + wrapper.execute(); + + Mockito.verify(builder, Mockito.times(1)).add(indexRequest); + Mockito.verify(builder, Mockito.times(1)).add(updateRequest); + Mockito.verify(builder, Mockito.times(1)).numberOfActions(); + Mockito.verify(builder, Mockito.times(1)).execute(); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java new file mode 100644 index 000000000..2bbb4ed67 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java @@ -0,0 +1,523 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.function.Supplier; + +import org.joda.time.DateTime; +import org.junit.Test; + +import com.netflix.conductor.common.metadata.events.EventExecution; +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.Workflow.WorkflowStatus; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.es7.utils.TestUtils; + +import com.google.common.collect.ImmutableMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestElasticSearchRestDAOV7 extends ElasticSearchRestDaoBaseTest { + + private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMWW"); + + private static final String INDEX_PREFIX = "conductor"; + private static final String WORKFLOW_DOC_TYPE = "workflow"; + private static final String TASK_DOC_TYPE = "task"; + private static final String MSG_DOC_TYPE = "message"; + private static final String EVENT_DOC_TYPE = "event"; + private static final String LOG_DOC_TYPE = "task_log"; + + private boolean indexExists(final String index) throws IOException { + return indexDAO.doesResourceExist("/" + index); + } + + private boolean doesMappingExist(final String index, final String mappingName) + throws IOException { + return indexDAO.doesResourceExist("/" + index + "/_mapping/" + mappingName); + } + + @Test + public void assertInitialSetup() throws IOException { + SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); + + String workflowIndex = INDEX_PREFIX + "_" + WORKFLOW_DOC_TYPE; + String taskIndex = INDEX_PREFIX + "_" + TASK_DOC_TYPE; + + String taskLogIndex = + INDEX_PREFIX + "_" + LOG_DOC_TYPE + "_" + SIMPLE_DATE_FORMAT.format(new Date()); + String messageIndex = + INDEX_PREFIX + "_" + MSG_DOC_TYPE + "_" + SIMPLE_DATE_FORMAT.format(new Date()); + String eventIndex = + INDEX_PREFIX + "_" + EVENT_DOC_TYPE + "_" + SIMPLE_DATE_FORMAT.format(new Date()); + + assertTrue("Index 'conductor_workflow' should exist", indexExists(workflowIndex)); + assertTrue("Index 'conductor_task' should exist", indexExists(taskIndex)); + + assertTrue("Index '" + taskLogIndex + "' should exist", indexExists(taskLogIndex)); + assertTrue("Index '" + messageIndex + "' should exist", indexExists(messageIndex)); + assertTrue("Index '" + eventIndex + "' should exist", indexExists(eventIndex)); + + assertTrue( + "Index template for 'message' should exist", + indexDAO.doesResourceExist("/_template/template_" + MSG_DOC_TYPE)); + assertTrue( + "Index template for 'event' should exist", + indexDAO.doesResourceExist("/_template/template_" + EVENT_DOC_TYPE)); + assertTrue( + "Index template for 'task_log' should exist", + indexDAO.doesResourceExist("/_template/template_" + LOG_DOC_TYPE)); + } + + @Test + public void shouldIndexWorkflow() { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + assertWorkflowSummary(workflowSummary.getWorkflowId(), workflowSummary); + } + + @Test + public void shouldIndexWorkflowAsync() throws Exception { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.asyncIndexWorkflow(workflowSummary).get(); + + assertWorkflowSummary(workflowSummary.getWorkflowId(), workflowSummary); + } + + @Test + public void shouldRemoveWorkflow() { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + // wait for workflow to be indexed + List workflows = + tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 1); + assertEquals(1, workflows.size()); + + indexDAO.removeWorkflow(workflowSummary.getWorkflowId()); + + workflows = tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 0); + + assertTrue("Workflow was not removed.", workflows.isEmpty()); + } + + @Test + public void shouldAsyncRemoveWorkflow() throws Exception { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + // wait for workflow to be indexed + List workflows = + tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 1); + assertEquals(1, workflows.size()); + + indexDAO.asyncRemoveWorkflow(workflowSummary.getWorkflowId()).get(); + + workflows = tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 0); + + assertTrue("Workflow was not removed.", workflows.isEmpty()); + } + + @Test + public void shouldUpdateWorkflow() { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + indexDAO.updateWorkflow( + workflowSummary.getWorkflowId(), + new String[] {"status"}, + new Object[] {WorkflowStatus.COMPLETED}); + + workflowSummary.setStatus(WorkflowStatus.COMPLETED); + assertWorkflowSummary(workflowSummary.getWorkflowId(), workflowSummary); + } + + @Test + public void shouldAsyncUpdateWorkflow() throws Exception { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + indexDAO.asyncUpdateWorkflow( + workflowSummary.getWorkflowId(), + new String[] {"status"}, + new Object[] {WorkflowStatus.FAILED}) + .get(); + + workflowSummary.setStatus(WorkflowStatus.FAILED); + assertWorkflowSummary(workflowSummary.getWorkflowId(), workflowSummary); + } + + @Test + public void shouldIndexTask() { + TaskSummary taskSummary = TestUtils.loadTaskSnapshot(objectMapper, "task_summary"); + indexDAO.indexTask(taskSummary); + + List tasks = tryFindResults(() -> searchTasks(taskSummary)); + + assertEquals(taskSummary.getTaskId(), tasks.get(0)); + } + + @Test + public void shouldIndexTaskAsync() throws Exception { + TaskSummary taskSummary = TestUtils.loadTaskSnapshot(objectMapper, "task_summary"); + indexDAO.asyncIndexTask(taskSummary).get(); + + List tasks = tryFindResults(() -> searchTasks(taskSummary)); + + assertEquals(taskSummary.getTaskId(), tasks.get(0)); + } + + @Test + public void shouldRemoveTask() { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + // wait for workflow to be indexed + tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 1); + + TaskSummary taskSummary = + TestUtils.loadTaskSnapshot( + objectMapper, "task_summary", workflowSummary.getWorkflowId()); + indexDAO.indexTask(taskSummary); + + // Wait for the task to be indexed + List tasks = tryFindResults(() -> searchTasks(taskSummary), 1); + + indexDAO.removeTask(workflowSummary.getWorkflowId(), taskSummary.getTaskId()); + + tasks = tryFindResults(() -> searchTasks(taskSummary), 0); + + assertTrue("Task was not removed.", tasks.isEmpty()); + } + + @Test + public void shouldAsyncRemoveTask() throws Exception { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + + // wait for workflow to be indexed + tryFindResults(() -> searchWorkflows(workflowSummary.getWorkflowId()), 1); + + TaskSummary taskSummary = + TestUtils.loadTaskSnapshot( + objectMapper, "task_summary", workflowSummary.getWorkflowId()); + indexDAO.indexTask(taskSummary); + + // Wait for the task to be indexed + List tasks = tryFindResults(() -> searchTasks(taskSummary), 1); + + indexDAO.asyncRemoveTask(workflowSummary.getWorkflowId(), taskSummary.getTaskId()).get(); + + tasks = tryFindResults(() -> searchTasks(taskSummary), 0); + + assertTrue("Task was not removed.", tasks.isEmpty()); + } + + @Test + public void shouldNotRemoveTaskWhenNotAssociatedWithWorkflow() { + TaskSummary taskSummary = TestUtils.loadTaskSnapshot(objectMapper, "task_summary"); + indexDAO.indexTask(taskSummary); + + // Wait for the task to be indexed + List tasks = tryFindResults(() -> searchTasks(taskSummary), 1); + + indexDAO.removeTask("InvalidWorkflow", taskSummary.getTaskId()); + + tasks = tryFindResults(() -> searchTasks(taskSummary), 0); + + assertFalse("Task was removed.", tasks.isEmpty()); + } + + @Test + public void shouldNotAsyncRemoveTaskWhenNotAssociatedWithWorkflow() throws Exception { + TaskSummary taskSummary = TestUtils.loadTaskSnapshot(objectMapper, "task_summary"); + indexDAO.indexTask(taskSummary); + + // Wait for the task to be indexed + List tasks = tryFindResults(() -> searchTasks(taskSummary), 1); + + indexDAO.asyncRemoveTask("InvalidWorkflow", taskSummary.getTaskId()).get(); + + tasks = tryFindResults(() -> searchTasks(taskSummary), 0); + + assertFalse("Task was removed.", tasks.isEmpty()); + } + + @Test + public void shouldAddTaskExecutionLogs() { + List logs = new ArrayList<>(); + String taskId = uuid(); + logs.add(createLog(taskId, "log1")); + logs.add(createLog(taskId, "log2")); + logs.add(createLog(taskId, "log3")); + + indexDAO.addTaskExecutionLogs(logs); + + List indexedLogs = + tryFindResults(() -> indexDAO.getTaskExecutionLogs(taskId), 3); + + assertEquals(3, indexedLogs.size()); + + assertTrue("Not all logs was indexed", indexedLogs.containsAll(logs)); + } + + @Test + public void shouldAddTaskExecutionLogsAsync() throws Exception { + List logs = new ArrayList<>(); + String taskId = uuid(); + logs.add(createLog(taskId, "log1")); + logs.add(createLog(taskId, "log2")); + logs.add(createLog(taskId, "log3")); + + indexDAO.asyncAddTaskExecutionLogs(logs).get(); + + List indexedLogs = + tryFindResults(() -> indexDAO.getTaskExecutionLogs(taskId), 3); + + assertEquals(3, indexedLogs.size()); + + assertTrue("Not all logs was indexed", indexedLogs.containsAll(logs)); + } + + @Test + public void shouldAddMessage() { + String queue = "queue"; + Message message1 = new Message(uuid(), "payload1", null); + Message message2 = new Message(uuid(), "payload2", null); + + indexDAO.addMessage(queue, message1); + indexDAO.addMessage(queue, message2); + + List indexedMessages = tryFindResults(() -> indexDAO.getMessages(queue), 2); + + assertEquals(2, indexedMessages.size()); + + assertTrue( + "Not all messages was indexed", + indexedMessages.containsAll(Arrays.asList(message1, message2))); + } + + @Test + public void shouldAddEventExecution() { + String event = "event"; + EventExecution execution1 = createEventExecution(event); + EventExecution execution2 = createEventExecution(event); + + indexDAO.addEventExecution(execution1); + indexDAO.addEventExecution(execution2); + + List indexedExecutions = + tryFindResults(() -> indexDAO.getEventExecutions(event), 2); + + assertEquals(2, indexedExecutions.size()); + + assertTrue( + "Not all event executions was indexed", + indexedExecutions.containsAll(Arrays.asList(execution1, execution2))); + } + + @Test + public void shouldAsyncAddEventExecution() throws Exception { + String event = "event2"; + EventExecution execution1 = createEventExecution(event); + EventExecution execution2 = createEventExecution(event); + + indexDAO.asyncAddEventExecution(execution1).get(); + indexDAO.asyncAddEventExecution(execution2).get(); + + List indexedExecutions = + tryFindResults(() -> indexDAO.getEventExecutions(event), 2); + + assertEquals(2, indexedExecutions.size()); + + assertTrue( + "Not all event executions was indexed", + indexedExecutions.containsAll(Arrays.asList(execution1, execution2))); + } + + @Test + public void shouldAddIndexPrefixToIndexTemplate() throws Exception { + String json = TestUtils.loadJsonResource("expected_template_task_log"); + String content = indexDAO.loadTypeMappingSource("/template_task_log.json"); + + assertEquals(json, content); + } + + @Test + public void shouldSearchRecentRunningWorkflows() throws Exception { + WorkflowSummary oldWorkflow = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + oldWorkflow.setStatus(WorkflowStatus.RUNNING); + oldWorkflow.setUpdateTime(getFormattedTime(new DateTime().minusHours(2).toDate())); + + WorkflowSummary recentWorkflow = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + recentWorkflow.setStatus(WorkflowStatus.RUNNING); + recentWorkflow.setUpdateTime(getFormattedTime(new DateTime().minusHours(1).toDate())); + + WorkflowSummary tooRecentWorkflow = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + tooRecentWorkflow.setStatus(WorkflowStatus.RUNNING); + tooRecentWorkflow.setUpdateTime(getFormattedTime(new DateTime().toDate())); + + indexDAO.indexWorkflow(oldWorkflow); + indexDAO.indexWorkflow(recentWorkflow); + indexDAO.indexWorkflow(tooRecentWorkflow); + + Thread.sleep(1000); + + List ids = indexDAO.searchRecentRunningWorkflows(2, 1); + + assertEquals(1, ids.size()); + assertEquals(recentWorkflow.getWorkflowId(), ids.get(0)); + } + + @Test + public void shouldCountWorkflows() { + int counts = 1100; + for (int i = 0; i < counts; i++) { + WorkflowSummary workflowSummary = + TestUtils.loadWorkflowSnapshot(objectMapper, "workflow_summary"); + indexDAO.indexWorkflow(workflowSummary); + } + + // wait for workflow to be indexed + long result = tryGetCount(() -> getWorkflowCount("template_workflow", "RUNNING"), counts); + assertEquals(counts, result); + } + + private long tryGetCount(Supplier countFunction, int resultsCount) { + long result = 0; + for (int i = 0; i < 20; i++) { + result = countFunction.get(); + if (result == resultsCount) { + return result; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return result; + } + + // Get total workflow counts given the name and status + private long getWorkflowCount(String workflowName, String status) { + return indexDAO.getWorkflowCount( + "status=\"" + status + "\" AND workflowType=\"" + workflowName + "\"", "*"); + } + + private void assertWorkflowSummary(String workflowId, WorkflowSummary summary) { + assertEquals(summary.getWorkflowType(), indexDAO.get(workflowId, "workflowType")); + assertEquals(String.valueOf(summary.getVersion()), indexDAO.get(workflowId, "version")); + assertEquals(summary.getWorkflowId(), indexDAO.get(workflowId, "workflowId")); + assertEquals(summary.getCorrelationId(), indexDAO.get(workflowId, "correlationId")); + assertEquals(summary.getStartTime(), indexDAO.get(workflowId, "startTime")); + assertEquals(summary.getUpdateTime(), indexDAO.get(workflowId, "updateTime")); + assertEquals(summary.getEndTime(), indexDAO.get(workflowId, "endTime")); + assertEquals(summary.getStatus().name(), indexDAO.get(workflowId, "status")); + assertEquals(summary.getInput(), indexDAO.get(workflowId, "input")); + assertEquals(summary.getOutput(), indexDAO.get(workflowId, "output")); + assertEquals( + summary.getReasonForIncompletion(), + indexDAO.get(workflowId, "reasonForIncompletion")); + assertEquals( + String.valueOf(summary.getExecutionTime()), + indexDAO.get(workflowId, "executionTime")); + assertEquals(summary.getEvent(), indexDAO.get(workflowId, "event")); + assertEquals( + summary.getFailedReferenceTaskNames(), + indexDAO.get(workflowId, "failedReferenceTaskNames")); + } + + private String getFormattedTime(Date time) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + return sdf.format(time); + } + + private List tryFindResults(Supplier> searchFunction) { + return tryFindResults(searchFunction, 1); + } + + private List tryFindResults(Supplier> searchFunction, int resultsCount) { + List result = Collections.emptyList(); + for (int i = 0; i < 20; i++) { + result = searchFunction.get(); + if (result.size() == resultsCount) { + return result; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return result; + } + + private List searchWorkflows(String workflowId) { + return indexDAO.searchWorkflows( + "", "workflowId:\"" + workflowId + "\"", 0, 100, Collections.emptyList()) + .getResults(); + } + + private List searchTasks(TaskSummary taskSummary) { + return indexDAO.searchTasks( + "", + "workflowId:\"" + taskSummary.getWorkflowId() + "\"", + 0, + 100, + Collections.emptyList()) + .getResults(); + } + + private TaskExecLog createLog(String taskId, String log) { + TaskExecLog taskExecLog = new TaskExecLog(log); + taskExecLog.setTaskId(taskId); + return taskExecLog; + } + + private EventExecution createEventExecution(String event) { + EventExecution execution = new EventExecution(uuid(), uuid()); + execution.setName("name"); + execution.setEvent(event); + execution.setCreated(System.currentTimeMillis()); + execution.setStatus(EventExecution.Status.COMPLETED); + execution.setAction(EventHandler.Action.Type.start_workflow); + execution.setOutput(ImmutableMap.of("a", 1, "b", 2, "c", 3)); + return execution; + } + + private String uuid() { + return UUID.randomUUID().toString(); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java new file mode 100644 index 000000000..df9ec6e95 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java @@ -0,0 +1,81 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.index; + +import java.util.HashMap; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; + +import com.netflix.conductor.common.metadata.tasks.Task.Status; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@TestPropertySource(properties = "conductor.elasticsearch.indexBatchSize=2") +public class TestElasticSearchRestDAOV7Batch extends ElasticSearchRestDaoBaseTest { + + @Test + public void indexTaskWithBatchSizeTwo() { + String correlationId = "some-correlation-id"; + + TaskSummary taskSummary = new TaskSummary(); + taskSummary.setTaskId("some-task-id"); + taskSummary.setWorkflowId("some-workflow-instance-id"); + taskSummary.setTaskType("some-task-type"); + taskSummary.setStatus(Status.FAILED); + try { + taskSummary.setInput( + objectMapper.writeValueAsString( + new HashMap() { + { + put("input_key", "input_value"); + } + })); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + taskSummary.setCorrelationId(correlationId); + taskSummary.setTaskDefName("some-task-def-name"); + taskSummary.setReasonForIncompletion("some-failure-reason"); + + indexDAO.indexTask(taskSummary); + indexDAO.indexTask(taskSummary); + + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted( + () -> { + SearchResult result = + indexDAO.searchTasks( + "correlationId='" + correlationId + "'", + "*", + 0, + 10000, + null); + + assertTrue( + "should return 1 or more search results", + result.getResults().size() > 0); + assertEquals( + "taskId should match the indexed task", + "some-task-id", + result.getResults().get(0)); + }); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java new file mode 100644 index 000000000..5ca4f0284 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java @@ -0,0 +1,149 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.junit.Test; + +import com.netflix.conductor.es7.dao.query.parser.internal.AbstractParserTest; +import com.netflix.conductor.es7.dao.query.parser.internal.ConstValue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Viren + */ +public class TestExpression extends AbstractParserTest { + + @Test + public void test() throws Exception { + String test = + "type='IMAGE' AND subType ='sdp' AND (metadata.width > 50 OR metadata.height > 50)"; + // test = "type='IMAGE' AND subType ='sdp'"; + // test = "(metadata.type = 'IMAGE')"; + InputStream is = new BufferedInputStream(new ByteArrayInputStream(test.getBytes())); + Expression expr = new Expression(is); + + System.out.println(expr); + + assertTrue(expr.isBinaryExpr()); + assertNull(expr.getGroupedExpression()); + assertNotNull(expr.getNameValue()); + + NameValue nv = expr.getNameValue(); + assertEquals("type", nv.getName().getName()); + assertEquals("=", nv.getOp().getOperator()); + assertEquals("\"IMAGE\"", nv.getValue().getValue()); + + Expression rhs = expr.getRightHandSide(); + assertNotNull(rhs); + assertTrue(rhs.isBinaryExpr()); + + nv = rhs.getNameValue(); + assertNotNull(nv); // subType = sdp + assertNull(rhs.getGroupedExpression()); + assertEquals("subType", nv.getName().getName()); + assertEquals("=", nv.getOp().getOperator()); + assertEquals("\"sdp\"", nv.getValue().getValue()); + + assertEquals("AND", rhs.getOperator().getOperator()); + rhs = rhs.getRightHandSide(); + assertNotNull(rhs); + assertFalse(rhs.isBinaryExpr()); + GroupedExpression ge = rhs.getGroupedExpression(); + assertNotNull(ge); + expr = ge.getExpression(); + assertNotNull(expr); + + assertTrue(expr.isBinaryExpr()); + nv = expr.getNameValue(); + assertNotNull(nv); + assertEquals("metadata.width", nv.getName().getName()); + assertEquals(">", nv.getOp().getOperator()); + assertEquals("50", nv.getValue().getValue()); + + assertEquals("OR", expr.getOperator().getOperator()); + rhs = expr.getRightHandSide(); + assertNotNull(rhs); + assertFalse(rhs.isBinaryExpr()); + nv = rhs.getNameValue(); + assertNotNull(nv); + + assertEquals("metadata.height", nv.getName().getName()); + assertEquals(">", nv.getOp().getOperator()); + assertEquals("50", nv.getValue().getValue()); + } + + @Test + public void testWithSysConstants() throws Exception { + String test = "type='IMAGE' AND subType ='sdp' AND description IS null"; + InputStream is = new BufferedInputStream(new ByteArrayInputStream(test.getBytes())); + Expression expr = new Expression(is); + + System.out.println(expr); + + assertTrue(expr.isBinaryExpr()); + assertNull(expr.getGroupedExpression()); + assertNotNull(expr.getNameValue()); + + NameValue nv = expr.getNameValue(); + assertEquals("type", nv.getName().getName()); + assertEquals("=", nv.getOp().getOperator()); + assertEquals("\"IMAGE\"", nv.getValue().getValue()); + + Expression rhs = expr.getRightHandSide(); + assertNotNull(rhs); + assertTrue(rhs.isBinaryExpr()); + + nv = rhs.getNameValue(); + assertNotNull(nv); // subType = sdp + assertNull(rhs.getGroupedExpression()); + assertEquals("subType", nv.getName().getName()); + assertEquals("=", nv.getOp().getOperator()); + assertEquals("\"sdp\"", nv.getValue().getValue()); + + assertEquals("AND", rhs.getOperator().getOperator()); + rhs = rhs.getRightHandSide(); + assertNotNull(rhs); + assertFalse(rhs.isBinaryExpr()); + GroupedExpression ge = rhs.getGroupedExpression(); + assertNull(ge); + nv = rhs.getNameValue(); + assertNotNull(nv); + assertEquals("description", nv.getName().getName()); + assertEquals("IS", nv.getOp().getOperator()); + ConstValue cv = nv.getValue(); + assertNotNull(cv); + assertEquals(cv.getSysConstant(), ConstValue.SystemConsts.NULL); + + test = "description IS not null"; + is = new BufferedInputStream(new ByteArrayInputStream(test.getBytes())); + expr = new Expression(is); + + System.out.println(expr); + nv = expr.getNameValue(); + assertNotNull(nv); + assertEquals("description", nv.getName().getName()); + assertEquals("IS", nv.getOp().getOperator()); + cv = nv.getValue(); + assertNotNull(cv); + assertEquals(cv.getSysConstant(), ConstValue.SystemConsts.NOT_NULL); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java new file mode 100644 index 000000000..ed79b0f91 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser; + +import org.junit.Test; + +/** + * @author Viren + */ +public class TestGroupedExpression { + + @Test + public void test() {} +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java new file mode 100644 index 000000000..412b7a8a6 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * @author Viren + */ +public abstract class AbstractParserTest { + + protected InputStream getInputStream(String expression) { + return new BufferedInputStream(new ByteArrayInputStream(expression.getBytes())); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java new file mode 100644 index 000000000..6f8c70e11 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Viren + */ +public class TestBooleanOp extends AbstractParserTest { + + @Test + public void test() throws Exception { + String[] tests = new String[] {"AND", "OR"}; + for (String test : tests) { + BooleanOp name = new BooleanOp(getInputStream(test)); + String nameVal = name.getOperator(); + assertNotNull(nameVal); + assertEquals(test, nameVal); + } + } + + @Test(expected = ParserException.class) + public void testInvalid() throws Exception { + String test = "<"; + BooleanOp name = new BooleanOp(getInputStream(test)); + String nameVal = name.getOperator(); + assertNotNull(nameVal); + assertEquals(test, nameVal); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java new file mode 100644 index 000000000..40c615f87 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Viren + */ +public class TestComparisonOp extends AbstractParserTest { + + @Test + public void test() throws Exception { + String[] tests = new String[] {"<", ">", "=", "!=", "IN", "BETWEEN", "STARTS_WITH"}; + for (String test : tests) { + ComparisonOp name = new ComparisonOp(getInputStream(test)); + String nameVal = name.getOperator(); + assertNotNull(nameVal); + assertEquals(test, nameVal); + } + } + + @Test(expected = ParserException.class) + public void testInvalidOp() throws Exception { + String test = "AND"; + ComparisonOp name = new ComparisonOp(getInputStream(test)); + String nameVal = name.getOperator(); + assertNotNull(nameVal); + assertEquals(test, nameVal); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java new file mode 100644 index 000000000..ee5769990 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java @@ -0,0 +1,101 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Viren + */ +public class TestConstValue extends AbstractParserTest { + + @Test + public void testStringConst() throws Exception { + String test = "'string value'"; + String expected = + test.replaceAll( + "'", "\""); // Quotes are removed but then the result is double quoted. + ConstValue cv = new ConstValue(getInputStream(test)); + assertNotNull(cv.getValue()); + assertEquals(expected, cv.getValue()); + assertTrue(cv.getValue() instanceof String); + + test = "\"string value\""; + cv = new ConstValue(getInputStream(test)); + assertNotNull(cv.getValue()); + assertEquals(expected, cv.getValue()); + assertTrue(cv.getValue() instanceof String); + } + + @Test + public void testSystemConst() throws Exception { + String test = "null"; + ConstValue cv = new ConstValue(getInputStream(test)); + assertNotNull(cv.getValue()); + assertTrue(cv.getValue() instanceof String); + assertEquals(cv.getSysConstant(), ConstValue.SystemConsts.NULL); + test = "null"; + + test = "not null"; + cv = new ConstValue(getInputStream(test)); + assertNotNull(cv.getValue()); + assertEquals(cv.getSysConstant(), ConstValue.SystemConsts.NOT_NULL); + } + + @Test(expected = ParserException.class) + public void testInvalid() throws Exception { + String test = "'string value"; + new ConstValue(getInputStream(test)); + } + + @Test + public void testNumConst() throws Exception { + String test = "12345.89"; + ConstValue cv = new ConstValue(getInputStream(test)); + assertNotNull(cv.getValue()); + assertTrue( + cv.getValue() + instanceof + String); // Numeric values are stored as string as we are just passing thru + // them to ES + assertEquals(test, cv.getValue()); + } + + @Test + public void testRange() throws Exception { + String test = "50 AND 100"; + Range range = new Range(getInputStream(test)); + assertEquals("50", range.getLow()); + assertEquals("100", range.getHigh()); + } + + @Test(expected = ParserException.class) + public void testBadRange() throws Exception { + String test = "50 AND"; + new Range(getInputStream(test)); + } + + @Test + public void testArray() throws Exception { + String test = "(1, 3, 'name', 'value2')"; + ListConst lc = new ListConst(getInputStream(test)); + List list = lc.getList(); + assertEquals(4, list.size()); + assertTrue(list.contains("1")); + assertEquals("'value2'", list.get(3)); // Values are preserved as it is... + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java new file mode 100644 index 000000000..26a192301 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.dao.query.parser.internal; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Viren + */ +public class TestName extends AbstractParserTest { + + @Test + public void test() throws Exception { + String test = "metadata.en_US.lang "; + Name name = new Name(getInputStream(test)); + String nameVal = name.getName(); + assertNotNull(nameVal); + assertEquals(test.trim(), nameVal); + } +} diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java new file mode 100644 index 000000000..5889cccd4 --- /dev/null +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.es7.utils; + +import org.apache.commons.io.Charsets; + +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.core.utils.IDGenerator; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.Resources; + +public class TestUtils { + + private static final String WORKFLOW_SCENARIO_EXTENSION = ".json"; + private static final String WORKFLOW_INSTANCE_ID_PLACEHOLDER = "WORKFLOW_INSTANCE_ID"; + + public static WorkflowSummary loadWorkflowSnapshot( + ObjectMapper objectMapper, String resourceFileName) { + try { + String content = loadJsonResource(resourceFileName); + String workflowId = new IDGenerator().generate(); + content = content.replace(WORKFLOW_INSTANCE_ID_PLACEHOLDER, workflowId); + + return objectMapper.readValue(content, WorkflowSummary.class); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public static TaskSummary loadTaskSnapshot(ObjectMapper objectMapper, String resourceFileName) { + try { + String content = loadJsonResource(resourceFileName); + String workflowId = new IDGenerator().generate(); + content = content.replace(WORKFLOW_INSTANCE_ID_PLACEHOLDER, workflowId); + + return objectMapper.readValue(content, TaskSummary.class); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public static TaskSummary loadTaskSnapshot( + ObjectMapper objectMapper, String resourceFileName, String workflowId) { + try { + String content = loadJsonResource(resourceFileName); + content = content.replace(WORKFLOW_INSTANCE_ID_PLACEHOLDER, workflowId); + + return objectMapper.readValue(content, TaskSummary.class); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public static String loadJsonResource(String resourceFileName) { + try { + return Resources.toString( + TestUtils.class.getResource( + "/" + resourceFileName + WORKFLOW_SCENARIO_EXTENSION), + Charsets.UTF_8); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } +} diff --git a/es7-persistence/src/test/resources/expected_template_task_log.json b/es7-persistence/src/test/resources/expected_template_task_log.json new file mode 100644 index 000000000..ebb8d4a20 --- /dev/null +++ b/es7-persistence/src/test/resources/expected_template_task_log.json @@ -0,0 +1,24 @@ +{ + "index_patterns" : [ "*conductor_task*log*" ], + "template" : { + "settings" : { + "refresh_interval" : "1s" + }, + "mappings" : { + "properties" : { + "createdTime" : { + "type" : "long" + }, + "log" : { + "type" : "keyword", + "index" : true + }, + "taskId" : { + "type" : "keyword", + "index" : true + } + } + }, + "aliases" : { } + } +} \ No newline at end of file diff --git a/es7-persistence/src/test/resources/task_summary.json b/es7-persistence/src/test/resources/task_summary.json new file mode 100644 index 000000000..a409a22f1 --- /dev/null +++ b/es7-persistence/src/test/resources/task_summary.json @@ -0,0 +1,17 @@ +{ + "taskId": "9dea4567-0240-4eab-bde8-99f4535ea3fc", + "taskDefName": "templated_task", + "taskType": "templated_task", + "workflowId": "WORKFLOW_INSTANCE_ID", + "workflowType": "template_workflow", + "correlationId": "testTaskDefTemplate", + "scheduledTime": "2021-08-22T05:18:25.121Z", + "startTime": "0", + "endTime": "0", + "updateTime": "2021-08-23T00:18:25.121Z", + "status": "SCHEDULED", + "workflowPriority": 1, + "queueWaitTime": 0, + "executionTime": 0, + "input": "{http_request={method=GET, vipStack=test_stack, body={requestDetails={key1=value1, key2=42}, outputPath=s3://bucket/outputPath, inputPaths=[file://path1, file://path2]}, uri=/get/something}}" +} \ No newline at end of file diff --git a/es7-persistence/src/test/resources/workflow_summary.json b/es7-persistence/src/test/resources/workflow_summary.json new file mode 100644 index 000000000..443d8464e --- /dev/null +++ b/es7-persistence/src/test/resources/workflow_summary.json @@ -0,0 +1,12 @@ +{ + "workflowType": "template_workflow", + "version": 1, + "workflowId": "WORKFLOW_INSTANCE_ID", + "priority": 1, + "correlationId": "testTaskDefTemplate", + "startTime": 1534983505050, + "updateTime": 1534983505131, + "endTime": 0, + "status": "RUNNING", + "input": "{path1=file://path1, path2=file://path2, requestDetails={key1=value1, key2=42}, outputPath=s3://bucket/outputPath}" +} diff --git a/settings.gradle b/settings.gradle index 8ecbf9288..a29f042d3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -68,6 +68,7 @@ include 'common-persistence' include 'mysql-persistence' include 'postgres-persistence' include 'metrics' +include 'es7-persistence' include 'test-harness' From ffd014c95ebda08a96768f4f161205b28b2cc557 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 18:58:56 -0800 Subject: [PATCH 038/202] es7 --- dependencies.gradle | 2 +- es7-persistence/dependencies.lock | 388 ++++++++++++++---- .../es7/dao/index/ElasticSearchRestDAOV7.java | 2 +- 3 files changed, 307 insertions(+), 85 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 42753f3ad..d8c1cbcfd 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -61,5 +61,5 @@ ext { revKafka = '2.6.0' revMicrometer = '1.6.2' revPrometheus = '0.9.0' - revElasticSearch7 = '7.17.13' + revElasticSearch7 = '7.12.1' } diff --git a/es7-persistence/dependencies.lock b/es7-persistence/dependencies.lock index 9411dbc82..4f4e8d446 100644 --- a/es7-persistence/dependencies.lock +++ b/es7-persistence/dependencies.lock @@ -1,27 +1,27 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "3.1.4" } }, "compileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-common-persistence": { "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true }, "commons-io:commons-io": { "locked": "2.7" @@ -30,54 +30,96 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.4" + "locked": "2.0.3" } }, "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-common-persistence": { "project": true @@ -86,80 +128,149 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common-persistence" ], - "locked": "3.15.0" + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" }, "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], "locked": "2.7" }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "7.17.15" + "locked": "7.12.1" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" } }, "shadow": { "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.4" + "locked": "2.0.3" } }, "testCompileClasspath": { "com.fasterxml.jackson.core:jackson-core": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { - "locked": "2.13.5" + "locked": "2.15.2" }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-common-persistence": { "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true }, "commons-io:commons-io": { "locked": "2.7" @@ -174,74 +285,116 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.4" + "locked": "2.0.3" }, "org.testcontainers:elasticsearch": { - "locked": "1.18.3" + "locked": "1.15.3" } }, "testRuntime": { "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.4" + "locked": "2.0.3" } }, "testRuntimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.13.5" + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-common-persistence": { "project": true @@ -250,76 +403,145 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common-persistence" ], - "locked": "3.15.0" + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" + }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" }, "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], "locked": "2.7" }, + "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "1.2.2" + }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, "junit:junit": { "locked": "4.13.2" }, "net.java.dev.jna:jna": { "locked": "5.7.0" }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-common-persistence" + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core" ], - "locked": "2.17.2" + "locked": "2.20.0" }, "org.awaitility:awaitility": { "locked": "3.1.6" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "7.17.15" + "locked": "7.12.1" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.retry:spring-retry": { - "locked": "1.3.4" + "locked": "2.0.3" }, "org.testcontainers:elasticsearch": { - "locked": "1.18.3" + "locked": "1.15.3" } } } \ No newline at end of file diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java index 05491872d..a137fd08d 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java @@ -42,6 +42,7 @@ import org.elasticsearch.client.*; import org.elasticsearch.client.core.CountRequest; import org.elasticsearch.client.core.CountResponse; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; @@ -50,7 +51,6 @@ import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; -import org.elasticsearch.xcontent.XContentType; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 1f59457b4320d42f0d2cee9743f84c37ad392b14 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 19:04:00 -0800 Subject: [PATCH 039/202] external payload store --- azureblob-storage/README.md | 44 +++ azureblob-storage/build.gradle | 8 + azureblob-storage/dependencies.lock | 150 +++++++++ .../config/AzureBlobConfiguration.java | 34 ++ .../azureblob/config/AzureBlobProperties.java | 123 +++++++ .../storage/AzureBlobPayloadStorage.java | 230 ++++++++++++++ .../storage/AzureBlobPayloadStorageTest.java | 158 +++++++++ dependencies.gradle | 3 + postgres-external-storage/README.md | 24 ++ postgres-external-storage/build.gradle | 18 ++ postgres-external-storage/dependencies.lock | 201 ++++++++++++ .../config/PostgresPayloadConfiguration.java | 82 +++++ .../config/PostgresPayloadProperties.java | 133 ++++++++ .../ExternalPostgresPayloadResource.java | 56 ++++ .../storage/PostgresPayloadStorage.java | 163 ++++++++++ .../R__initial_schema.sql | 56 ++++ .../ExternalPostgresPayloadResourceTest.java | 58 ++++ .../storage/PostgresPayloadStorageTest.java | 299 ++++++++++++++++++ .../storage/PostgresPayloadTestUtil.java | 73 +++++ settings.gradle | 2 + 20 files changed, 1915 insertions(+) create mode 100644 azureblob-storage/README.md create mode 100644 azureblob-storage/build.gradle create mode 100644 azureblob-storage/dependencies.lock create mode 100644 azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java create mode 100644 azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java create mode 100644 azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java create mode 100644 azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java create mode 100644 postgres-external-storage/README.md create mode 100644 postgres-external-storage/build.gradle create mode 100644 postgres-external-storage/dependencies.lock create mode 100644 postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java create mode 100644 postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java create mode 100644 postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java create mode 100644 postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java create mode 100644 postgres-external-storage/src/main/resources/db/migration_external_postgres/R__initial_schema.sql create mode 100644 postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java create mode 100644 postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java create mode 100644 postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java diff --git a/azureblob-storage/README.md b/azureblob-storage/README.md new file mode 100644 index 000000000..33a39349c --- /dev/null +++ b/azureblob-storage/README.md @@ -0,0 +1,44 @@ +# Azure Blob External Storage Module + +This module use azure blob to store and retrieve workflows/tasks input/output payload that +went over the thresholds defined in properties named `conductor.[workflow|task].[input|output].payload.threshold.kb`. + +**Warning** Azure Java SDK use libs already present inside `conductor` like `jackson` and `netty`. +You may encounter deprecated issues, or conflicts and need to adapt the code if the module is not maintained along with `conductor`. +It has only been tested with **v12.2.0**. + +## Configuration + +### Usage + +Cf. Documentation [External Payload Storage](https://netflix.github.io/conductor/externalpayloadstorage/#azure-blob-storage) + +### Example + +```properties +conductor.additional.modules=com.netflix.conductor.azureblob.AzureBlobModule +es.set.netty.runtime.available.processors=false + +workflow.external.payload.storage=AZURE_BLOB +workflow.external.payload.storage.azure_blob.connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;EndpointSuffix=localhost +workflow.external.payload.storage.azure_blob.signedurlexpirationseconds=360 +``` + +## Testing + +You can use [Azurite](https://github.com/Azure/Azurite) to simulate an Azure Storage. + +### Troubleshoots + +* When using **es5 persistance** you will receive an `java.lang.IllegalStateException` because the Netty lib will call `setAvailableProcessors` two times. To resolve this issue you need to set the following system property + +``` +es.set.netty.runtime.available.processors=false +``` + +If you want to change the default HTTP client of azure sdk, you can use `okhttp` instead of `netty`. +For that you need to add the following [dependency](https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/storage/azure-storage-blob#default-http-client). + +``` +com.azure:azure-core-http-okhttp:${compatible version} +``` diff --git a/azureblob-storage/build.gradle b/azureblob-storage/build.gradle new file mode 100644 index 000000000..8e4b4627b --- /dev/null +++ b/azureblob-storage/build.gradle @@ -0,0 +1,8 @@ +dependencies { + compileOnly 'org.springframework.boot:spring-boot-starter' + implementation project(':conductor-common') + implementation project(':conductor-core') + + implementation "com.azure:azure-storage-blob:${revAzureStorageBlobSdk}" + implementation "org.apache.commons:commons-lang3" +} diff --git a/azureblob-storage/dependencies.lock b/azureblob-storage/dependencies.lock new file mode 100644 index 000000000..9691cd2d6 --- /dev/null +++ b/azureblob-storage/dependencies.lock @@ -0,0 +1,150 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.azure:azure-storage-blob": { + "locked": "12.7.0" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.azure:azure-storage-blob": { + "locked": "12.7.0" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.azure:azure-storage-blob": { + "locked": "12.7.0" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + }, + "testRuntimeClasspath": { + "com.azure:azure-storage-blob": { + "locked": "12.7.0" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + } +} \ No newline at end of file diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java new file mode 100644 index 000000000..476ad1dad --- /dev/null +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.azureblob.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.azureblob.storage.AzureBlobPayloadStorage; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.utils.IDGenerator; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(AzureBlobProperties.class) +@ConditionalOnProperty(name = "conductor.external-payload-storage.type", havingValue = "azureblob") +public class AzureBlobConfiguration { + + @Bean + public ExternalPayloadStorage azureBlobExternalPayloadStorage( + IDGenerator idGenerator, AzureBlobProperties properties) { + return new AzureBlobPayloadStorage(idGenerator, properties); + } +} diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java new file mode 100644 index 000000000..cb5450923 --- /dev/null +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java @@ -0,0 +1,123 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.azureblob.config; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; + +@ConfigurationProperties("conductor.external-payload-storage.azureblob") +public class AzureBlobProperties { + + /** The connection string to be used to connect to Azure Blob storage */ + private String connectionString = null; + + /** The name of the container where the payloads will be stored */ + private String containerName = "conductor-payloads"; + + /** The endpoint to be used to connect to Azure Blob storage */ + private String endpoint = null; + + /** The sas token to be used for authenticating requests */ + private String sasToken = null; + + /** The time for which the shared access signature is valid */ + @DurationUnit(ChronoUnit.SECONDS) + private Duration signedUrlExpirationDuration = Duration.ofSeconds(5); + + /** The path at which the workflow inputs will be stored */ + private String workflowInputPath = "workflow/input/"; + + /** The path at which the workflow outputs will be stored */ + private String workflowOutputPath = "workflow/output/"; + + /** The path at which the task inputs will be stored */ + private String taskInputPath = "task/input/"; + + /** The path at which the task outputs will be stored */ + private String taskOutputPath = "task/output/"; + + public String getConnectionString() { + return connectionString; + } + + public void setConnectionString(String connectionString) { + this.connectionString = connectionString; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getSasToken() { + return sasToken; + } + + public void setSasToken(String sasToken) { + this.sasToken = sasToken; + } + + public Duration getSignedUrlExpirationDuration() { + return signedUrlExpirationDuration; + } + + public void setSignedUrlExpirationDuration(Duration signedUrlExpirationDuration) { + this.signedUrlExpirationDuration = signedUrlExpirationDuration; + } + + public String getWorkflowInputPath() { + return workflowInputPath; + } + + public void setWorkflowInputPath(String workflowInputPath) { + this.workflowInputPath = workflowInputPath; + } + + public String getWorkflowOutputPath() { + return workflowOutputPath; + } + + public void setWorkflowOutputPath(String workflowOutputPath) { + this.workflowOutputPath = workflowOutputPath; + } + + public String getTaskInputPath() { + return taskInputPath; + } + + public void setTaskInputPath(String taskInputPath) { + this.taskInputPath = taskInputPath; + } + + public String getTaskOutputPath() { + return taskOutputPath; + } + + public void setTaskOutputPath(String taskOutputPath) { + this.taskOutputPath = taskOutputPath; + } +} diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java new file mode 100644 index 000000000..4ad51fb33 --- /dev/null +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java @@ -0,0 +1,230 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.azureblob.storage; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.azureblob.config.AzureBlobProperties; +import com.netflix.conductor.common.run.ExternalStorageLocation; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.core.utils.IDGenerator; + +import com.azure.core.exception.UnexpectedLengthException; +import com.azure.core.util.Context; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobContainerClientBuilder; +import com.azure.storage.blob.models.BlobHttpHeaders; +import com.azure.storage.blob.models.BlobStorageException; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; +import com.azure.storage.blob.specialized.BlockBlobClient; +import com.azure.storage.common.Utility; +import com.azure.storage.common.implementation.credentials.SasTokenCredential; + +/** + * An implementation of {@link ExternalPayloadStorage} using Azure Blob for storing large JSON + * payload data. + * + * @see Azure Java SDK + */ +public class AzureBlobPayloadStorage implements ExternalPayloadStorage { + + private static final Logger LOGGER = LoggerFactory.getLogger(AzureBlobPayloadStorage.class); + private static final String CONTENT_TYPE = "application/json"; + + private final IDGenerator idGenerator; + private final String workflowInputPath; + private final String workflowOutputPath; + private final String taskInputPath; + private final String taskOutputPath; + + private final BlobContainerClient blobContainerClient; + private final long expirationSec; + private final SasTokenCredential sasTokenCredential; + + public AzureBlobPayloadStorage(IDGenerator idGenerator, AzureBlobProperties properties) { + this.idGenerator = idGenerator; + workflowInputPath = properties.getWorkflowInputPath(); + workflowOutputPath = properties.getWorkflowOutputPath(); + taskInputPath = properties.getTaskInputPath(); + taskOutputPath = properties.getTaskOutputPath(); + expirationSec = properties.getSignedUrlExpirationDuration().getSeconds(); + String connectionString = properties.getConnectionString(); + String containerName = properties.getContainerName(); + String endpoint = properties.getEndpoint(); + String sasToken = properties.getSasToken(); + + BlobContainerClientBuilder blobContainerClientBuilder = new BlobContainerClientBuilder(); + if (connectionString != null) { + blobContainerClientBuilder.connectionString(connectionString); + sasTokenCredential = null; + } else if (endpoint != null) { + blobContainerClientBuilder.endpoint(endpoint); + if (sasToken != null) { + sasTokenCredential = SasTokenCredential.fromSasTokenString(sasToken); + blobContainerClientBuilder.sasToken(sasTokenCredential.getSasToken()); + } else { + sasTokenCredential = null; + } + } else { + String msg = "Missing property for connectionString OR endpoint"; + LOGGER.error(msg); + throw new NonTransientException(msg); + } + blobContainerClient = blobContainerClientBuilder.containerName(containerName).buildClient(); + } + + /** + * @param operation the type of {@link Operation} to be performed + * @param payloadType the {@link PayloadType} that is being accessed + * @return a {@link ExternalStorageLocation} object which contains the pre-signed URL and the + * azure blob name for the json payload + */ + @Override + public ExternalStorageLocation getLocation( + Operation operation, PayloadType payloadType, String path) { + try { + ExternalStorageLocation externalStorageLocation = new ExternalStorageLocation(); + + String objectKey; + if (StringUtils.isNotBlank(path)) { + objectKey = path; + } else { + objectKey = getObjectKey(payloadType); + } + externalStorageLocation.setPath(objectKey); + + BlockBlobClient blockBlobClient = + blobContainerClient.getBlobClient(objectKey).getBlockBlobClient(); + String blobUrl = Utility.urlDecode(blockBlobClient.getBlobUrl()); + + if (sasTokenCredential != null) { + blobUrl = blobUrl + "?" + sasTokenCredential.getSasToken(); + } else { + BlobSasPermission blobSASPermission = new BlobSasPermission(); + if (operation.equals(Operation.READ)) { + blobSASPermission.setReadPermission(true); + } else if (operation.equals(Operation.WRITE)) { + blobSASPermission.setWritePermission(true); + blobSASPermission.setCreatePermission(true); + } + BlobServiceSasSignatureValues blobServiceSasSignatureValues = + new BlobServiceSasSignatureValues( + OffsetDateTime.now(ZoneOffset.UTC).plusSeconds(expirationSec), + blobSASPermission); + blobUrl = + blobUrl + "?" + blockBlobClient.generateSas(blobServiceSasSignatureValues); + } + + externalStorageLocation.setUri(blobUrl); + return externalStorageLocation; + } catch (BlobStorageException e) { + String msg = "Error communicating with Azure"; + LOGGER.error(msg, e); + throw new NonTransientException(msg, e); + } + } + + /** + * Uploads the payload to the given azure blob name. It is expected that the caller retrieves + * the blob name using {@link #getLocation(Operation, PayloadType, String)} before making this + * call. + * + * @param path the name of the blob to be uploaded + * @param payload an {@link InputStream} containing the json payload which is to be uploaded + * @param payloadSize the size of the json payload in bytes + */ + @Override + public void upload(String path, InputStream payload, long payloadSize) { + try { + BlockBlobClient blockBlobClient = + blobContainerClient.getBlobClient(path).getBlockBlobClient(); + BlobHttpHeaders blobHttpHeaders = new BlobHttpHeaders().setContentType(CONTENT_TYPE); + blockBlobClient.uploadWithResponse( + payload, + payloadSize, + blobHttpHeaders, + null, + null, + null, + null, + null, + Context.NONE); + } catch (BlobStorageException | UncheckedIOException | UnexpectedLengthException e) { + String msg = "Error communicating with Azure"; + LOGGER.error(msg, e); + throw new NonTransientException(msg, e); + } + } + + /** + * Downloads the payload stored in an azure blob. + * + * @param path the path of the blob + * @return an input stream containing the contents of the object Caller is expected to close the + * input stream. + */ + @Override + public InputStream download(String path) { + try { + BlockBlobClient blockBlobClient = + blobContainerClient.getBlobClient(path).getBlockBlobClient(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // Avoid another call to the api to get the blob size + // ByteArrayOutputStream outputStream = new + // ByteArrayOutputStream(blockBlobClient.getProperties().value().blobSize()); + blockBlobClient.download(outputStream); + return new ByteArrayInputStream(outputStream.toByteArray()); + } catch (BlobStorageException | UncheckedIOException | NullPointerException e) { + String msg = "Error communicating with Azure"; + LOGGER.error(msg, e); + throw new NonTransientException(msg, e); + } + } + + /** + * Build path on external storage. Copied from S3PayloadStorage. + * + * @param payloadType the {@link PayloadType} which will determine the base path of the object + * @return External Storage path + */ + private String getObjectKey(PayloadType payloadType) { + StringBuilder stringBuilder = new StringBuilder(); + switch (payloadType) { + case WORKFLOW_INPUT: + stringBuilder.append(workflowInputPath); + break; + case WORKFLOW_OUTPUT: + stringBuilder.append(workflowOutputPath); + break; + case TASK_INPUT: + stringBuilder.append(taskInputPath); + break; + case TASK_OUTPUT: + stringBuilder.append(taskOutputPath); + break; + } + stringBuilder.append(idGenerator.generate()).append(".json"); + return stringBuilder.toString(); + } +} diff --git a/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java new file mode 100644 index 000000000..b5fb30aee --- /dev/null +++ b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.azureblob.storage; + +import java.time.Duration; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.netflix.conductor.azureblob.config.AzureBlobProperties; +import com.netflix.conductor.common.run.ExternalStorageLocation; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.core.utils.IDGenerator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AzureBlobPayloadStorageTest { + + private AzureBlobProperties properties; + + private IDGenerator idGenerator; + + @Before + public void setUp() { + properties = mock(AzureBlobProperties.class); + idGenerator = new IDGenerator(); + when(properties.getConnectionString()).thenReturn(null); + when(properties.getContainerName()).thenReturn("conductor-payloads"); + when(properties.getEndpoint()).thenReturn(null); + when(properties.getSasToken()).thenReturn(null); + when(properties.getSignedUrlExpirationDuration()).thenReturn(Duration.ofSeconds(5)); + when(properties.getWorkflowInputPath()).thenReturn("workflow/input/"); + when(properties.getWorkflowOutputPath()).thenReturn("workflow/output/"); + when(properties.getTaskInputPath()).thenReturn("task/input"); + when(properties.getTaskOutputPath()).thenReturn("task/output/"); + } + + /** Dummy credentials Azure SDK doesn't work with Azurite since it cleans parameters */ + private final String azuriteConnectionString = + "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;EndpointSuffix=localhost"; + + @Rule public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void testNoStorageAccount() { + expectedException.expect(NonTransientException.class); + new AzureBlobPayloadStorage(idGenerator, properties); + } + + @Test + public void testUseConnectionString() { + when(properties.getConnectionString()).thenReturn(azuriteConnectionString); + new AzureBlobPayloadStorage(idGenerator, properties); + } + + @Test + public void testUseEndpoint() { + String azuriteEndpoint = "http://127.0.0.1:10000/"; + when(properties.getEndpoint()).thenReturn(azuriteEndpoint); + new AzureBlobPayloadStorage(idGenerator, properties); + } + + @Test + public void testGetLocationFixedPath() { + when(properties.getConnectionString()).thenReturn(azuriteConnectionString); + AzureBlobPayloadStorage azureBlobPayloadStorage = + new AzureBlobPayloadStorage(idGenerator, properties); + String path = "somewhere"; + ExternalStorageLocation externalStorageLocation = + azureBlobPayloadStorage.getLocation( + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.WORKFLOW_INPUT, + path); + assertNotNull(externalStorageLocation); + assertEquals(path, externalStorageLocation.getPath()); + assertNotNull(externalStorageLocation.getUri()); + } + + private void testGetLocation( + AzureBlobPayloadStorage azureBlobPayloadStorage, + ExternalPayloadStorage.Operation operation, + ExternalPayloadStorage.PayloadType payloadType, + String expectedPath) { + ExternalStorageLocation externalStorageLocation = + azureBlobPayloadStorage.getLocation(operation, payloadType, null); + assertNotNull(externalStorageLocation); + assertNotNull(externalStorageLocation.getPath()); + assertTrue(externalStorageLocation.getPath().startsWith(expectedPath)); + assertNotNull(externalStorageLocation.getUri()); + assertTrue(externalStorageLocation.getUri().contains(expectedPath)); + } + + @Test + public void testGetAllLocations() { + when(properties.getConnectionString()).thenReturn(azuriteConnectionString); + AzureBlobPayloadStorage azureBlobPayloadStorage = + new AzureBlobPayloadStorage(idGenerator, properties); + + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.WORKFLOW_INPUT, + properties.getWorkflowInputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.WORKFLOW_OUTPUT, + properties.getWorkflowOutputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.TASK_INPUT, + properties.getTaskInputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.TASK_OUTPUT, + properties.getTaskOutputPath()); + + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.WRITE, + ExternalPayloadStorage.PayloadType.WORKFLOW_INPUT, + properties.getWorkflowInputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.WRITE, + ExternalPayloadStorage.PayloadType.WORKFLOW_OUTPUT, + properties.getWorkflowOutputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.WRITE, + ExternalPayloadStorage.PayloadType.TASK_INPUT, + properties.getTaskInputPath()); + testGetLocation( + azureBlobPayloadStorage, + ExternalPayloadStorage.Operation.WRITE, + ExternalPayloadStorage.PayloadType.TASK_OUTPUT, + properties.getTaskOutputPath()); + } +} diff --git a/dependencies.gradle b/dependencies.gradle index d8c1cbcfd..63ae1a084 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -62,4 +62,7 @@ ext { revMicrometer = '1.6.2' revPrometheus = '0.9.0' revElasticSearch7 = '7.12.1' + revCodec = '1.15' + revAzureStorageBlobSdk = '12.7.0' + } diff --git a/postgres-external-storage/README.md b/postgres-external-storage/README.md new file mode 100644 index 000000000..341d545c3 --- /dev/null +++ b/postgres-external-storage/README.md @@ -0,0 +1,24 @@ +# PostgreSQL External Storage Module + +This module use PostgreSQL to store and retrieve workflows/tasks input/output payload that +went over the thresholds defined in properties named `conductor.[workflow|task].[input|output].payload.threshold.kb`. + +## Configuration + +### Usage + +Cf. Documentation [External Payload Storage](https://netflix.github.io/conductor/externalpayloadstorage/#postgresql-storage) + +### Example + +```properties +conductor.external-payload-storage.type=postgres +conductor.external-payload-storage.postgres.conductor-url=http://localhost:8080 +conductor.external-payload-storage.postgres.url=jdbc:postgresql://postgresql:5432/conductor?charset=utf8&parseTime=true&interpolateParams=true +conductor.external-payload-storage.postgres.username=postgres +conductor.external-payload-storage.postgres.password=postgres +conductor.external-payload-storage.postgres.max-data-rows=1000000 +conductor.external-payload-storage.postgres.max-data-days=0 +conductor.external-payload-storage.postgres.max-data-months=0 +conductor.external-payload-storage.postgres.max-data-years=1 +``` \ No newline at end of file diff --git a/postgres-external-storage/build.gradle b/postgres-external-storage/build.gradle new file mode 100644 index 000000000..9d103eca7 --- /dev/null +++ b/postgres-external-storage/build.gradle @@ -0,0 +1,18 @@ +dependencies { + implementation project(':conductor-common') + implementation project(':conductor-core') + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + + implementation 'org.postgresql:postgresql' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.flywaydb:flyway-core' + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${revSpringDoc}" + implementation "commons-codec:commons-codec:${revCodec}" + + testImplementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation "org.testcontainers:postgresql:${revTestContainer}" + testImplementation project(':conductor-test-util').sourceSets.test.output + +} diff --git a/postgres-external-storage/dependencies.lock b/postgres-external-storage/dependencies.lock new file mode 100644 index 000000000..34362330d --- /dev/null +++ b/postgres-external-storage/dependencies.lock @@ -0,0 +1,201 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-codec:commons-codec": { + "locked": "1.15" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.flywaydb:flyway-core": { + "locked": "8.5.13" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springdoc:springdoc-openapi-ui": { + "locked": "1.6.15" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-codec:commons-codec": { + "locked": "1.15" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.flywaydb:flyway-core": { + "locked": "8.5.13" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springdoc:springdoc-openapi-ui": { + "locked": "1.6.15" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "2.7.16" + } + }, + "testCompileClasspath": { + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-codec:commons-codec": { + "locked": "1.15" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.flywaydb:flyway-core": { + "locked": "8.5.13" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springdoc:springdoc-openapi-ui": { + "locked": "1.6.15" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + }, + "org.testcontainers:postgresql": { + "locked": "1.18.3" + } + }, + "testRuntimeClasspath": { + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "commons-codec:commons-codec": { + "locked": "1.15" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.flywaydb:flyway-core": { + "locked": "8.5.13" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.postgresql:postgresql": { + "locked": "42.3.8" + }, + "org.springdoc:springdoc-openapi-ui": { + "locked": "1.6.15" + }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + }, + "org.testcontainers:postgresql": { + "locked": "1.18.3" + } + } +} \ No newline at end of file diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java new file mode 100644 index 000000000..c9e5819f5 --- /dev/null +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java @@ -0,0 +1,82 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.config; + +import java.util.Map; + +import javax.sql.DataSource; + +import jakarta.annotation.*; +import org.flywaydb.core.Flyway; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Import; + +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.utils.IDGenerator; +import com.netflix.conductor.postgres.storage.PostgresPayloadStorage; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(PostgresPayloadProperties.class) +@ConditionalOnProperty(name = "conductor.external-payload-storage.type", havingValue = "postgres") +@Import(DataSourceAutoConfiguration.class) +public class PostgresPayloadConfiguration { + + PostgresPayloadProperties properties; + DataSource dataSource; + IDGenerator idGenerator; + private static final String DEFAULT_MESSAGE_TO_USER = + "{\"Error\": \"Data with this ID does not exist or has been deleted from the external storage.\"}"; + + public PostgresPayloadConfiguration( + PostgresPayloadProperties properties, DataSource dataSource, IDGenerator idGenerator) { + this.properties = properties; + this.dataSource = dataSource; + this.idGenerator = idGenerator; + } + + @Bean(initMethod = "migrate") + @PostConstruct + public Flyway flywayForExternalDb() { + return Flyway.configure() + .locations("classpath:db/migration_external_postgres") + .schemas("external") + .baselineOnMigrate(true) + .placeholderReplacement(true) + .placeholders( + Map.of( + "tableName", + properties.getTableName(), + "maxDataRows", + String.valueOf(properties.getMaxDataRows()), + "maxDataDays", + "'" + properties.getMaxDataDays() + "'", + "maxDataMonths", + "'" + properties.getMaxDataMonths() + "'", + "maxDataYears", + "'" + properties.getMaxDataYears() + "'")) + .dataSource(dataSource) + .load(); + } + + @Bean + @DependsOn({"flywayForExternalDb"}) + public ExternalPayloadStorage postgresExternalPayloadStorage( + PostgresPayloadProperties properties) { + return new PostgresPayloadStorage( + properties, dataSource, idGenerator, DEFAULT_MESSAGE_TO_USER); + } +} diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java new file mode 100644 index 000000000..f96ec98f5 --- /dev/null +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java @@ -0,0 +1,133 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("conductor.external-payload-storage.postgres") +public class PostgresPayloadProperties { + + /** The PostgreSQL schema and table name where the payloads will be stored */ + private String tableName = "external.external_payload"; + + /** Username for connecting to PostgreSQL database */ + private String username; + + /** Password for connecting to PostgreSQL database */ + private String password; + + /** URL for connecting to PostgreSQL database */ + private String url; + + /** + * Maximum count of data rows in PostgreSQL database. After overcoming this limit, the oldest + * data will be deleted. + */ + private long maxDataRows = Long.MAX_VALUE; + + /** + * Maximum count of days of data age in PostgreSQL database. After overcoming limit, the oldest + * data will be deleted. + */ + private int maxDataDays = 0; + + /** + * Maximum count of months of data age in PostgreSQL database. After overcoming limit, the + * oldest data will be deleted. + */ + private int maxDataMonths = 0; + + /** + * Maximum count of years of data age in PostgreSQL database. After overcoming limit, the oldest + * data will be deleted. + */ + private int maxDataYears = 1; + + /** + * URL, that can be used to pull the json configurations, that will be downloaded from + * PostgreSQL to the conductor server. For example: for local development it is + * "http://localhost:8080" + */ + private String conductorUrl = ""; + + public String getTableName() { + return tableName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getUrl() { + return url; + } + + public String getConductorUrl() { + return conductorUrl; + } + + public long getMaxDataRows() { + return maxDataRows; + } + + public int getMaxDataDays() { + return maxDataDays; + } + + public int getMaxDataMonths() { + return maxDataMonths; + } + + public int getMaxDataYears() { + return maxDataYears; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setConductorUrl(String conductorUrl) { + this.conductorUrl = conductorUrl; + } + + public void setMaxDataRows(long maxDataRows) { + this.maxDataRows = maxDataRows; + } + + public void setMaxDataDays(int maxDataDays) { + this.maxDataDays = maxDataDays; + } + + public void setMaxDataMonths(int maxDataMonths) { + this.maxDataMonths = maxDataMonths; + } + + public void setMaxDataYears(int maxDataYears) { + this.maxDataYears = maxDataYears; + } +} diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java new file mode 100644 index 000000000..1aeab3aae --- /dev/null +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java @@ -0,0 +1,56 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.controller; + +import java.io.InputStream; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.netflix.conductor.common.utils.ExternalPayloadStorage; + +import io.swagger.v3.oas.annotations.Operation; + +/** + * REST controller for pulling payload stream of data by key (externalPayloadPath) from PostgreSQL + * database + */ +@RestController +@RequestMapping(value = "/api/external/postgres") +@ConditionalOnProperty(name = "conductor.external-payload-storage.type", havingValue = "postgres") +public class ExternalPostgresPayloadResource { + + private final ExternalPayloadStorage postgresService; + + public ExternalPostgresPayloadResource( + @Qualifier("postgresExternalPayloadStorage") ExternalPayloadStorage postgresService) { + this.postgresService = postgresService; + } + + @GetMapping("/{externalPayloadPath}") + @Operation( + summary = + "Get task or workflow by externalPayloadPath from External PostgreSQL Storage") + public ResponseEntity getExternalStorageData( + @PathVariable("externalPayloadPath") String externalPayloadPath) { + InputStream inputStream = postgresService.download(externalPayloadPath); + InputStreamResource outputStreamBody = new InputStreamResource(inputStream); + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(outputStreamBody); + } +} diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java new file mode 100644 index 000000000..d71998ed9 --- /dev/null +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java @@ -0,0 +1,163 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.storage; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.function.Supplier; + +import javax.sql.DataSource; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.run.ExternalStorageLocation; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.exception.NonTransientException; +import com.netflix.conductor.core.utils.IDGenerator; +import com.netflix.conductor.postgres.config.PostgresPayloadProperties; + +/** + * Store and pull the external payload which consists of key and stream of data in PostgreSQL + * database + */ +public class PostgresPayloadStorage implements ExternalPayloadStorage { + + private static final Logger LOGGER = LoggerFactory.getLogger(PostgresPayloadStorage.class); + public static final String URI_SUFFIX_HASHED = ".hashed.json"; + public static final String URI_SUFFIX = ".json"; + public static final String URI_PREFIX_EXTERNAL = "/api/external/postgres/"; + private final String defaultMessageToUser; + + private final DataSource postgresDataSource; + + private final IDGenerator idGenerator; + + private final String tableName; + private final String conductorUrl; + + public PostgresPayloadStorage( + PostgresPayloadProperties properties, + DataSource dataSource, + IDGenerator idGenerator, + String defaultMessageToUser) { + tableName = properties.getTableName(); + conductorUrl = properties.getConductorUrl(); + this.postgresDataSource = dataSource; + this.idGenerator = idGenerator; + this.defaultMessageToUser = defaultMessageToUser; + LOGGER.info("PostgreSQL Extenal Payload Storage initialized."); + } + + /** + * @param operation the type of {@link Operation} to be performed + * @param payloadType the {@link PayloadType} that is being accessed + * @return a {@link ExternalStorageLocation} object which contains the pre-signed URL and the + * PostgreSQL object key for the json payload + */ + @Override + public ExternalStorageLocation getLocation( + Operation operation, PayloadType payloadType, String path) { + + return getLocationInternal(path, () -> idGenerator.generate() + URI_SUFFIX); + } + + @Override + public ExternalStorageLocation getLocation( + Operation operation, PayloadType payloadType, String path, byte[] payloadBytes) { + + return getLocationInternal( + path, () -> DigestUtils.sha256Hex(payloadBytes) + URI_SUFFIX_HASHED); + } + + private ExternalStorageLocation getLocationInternal( + String path, Supplier calculateKey) { + ExternalStorageLocation externalStorageLocation = new ExternalStorageLocation(); + String objectKey; + if (StringUtils.isNotBlank(path)) { + objectKey = path; + } else { + objectKey = calculateKey.get(); + } + String uri = conductorUrl + URI_PREFIX_EXTERNAL + objectKey; + externalStorageLocation.setUri(uri); + externalStorageLocation.setPath(objectKey); + LOGGER.debug("External storage location URI: {}, location path: {}", uri, objectKey); + return externalStorageLocation; + } + + /** + * Uploads the payload to the given PostgreSQL object key. It is expected that the caller + * retrieves the object key using {@link #getLocation(Operation, PayloadType, String)} before + * making this call. + * + * @param key the PostgreSQL key of the object to be uploaded + * @param payload an {@link InputStream} containing the json payload which is to be uploaded + * @param payloadSize the size of the json payload in bytes + */ + @Override + public void upload(String key, InputStream payload, long payloadSize) { + try (Connection conn = postgresDataSource.getConnection(); + PreparedStatement stmt = + conn.prepareStatement( + "INSERT INTO " + + tableName + + " (id, data) VALUES (?, ?) ON CONFLICT(id) " + + "DO UPDATE SET created_on=CURRENT_TIMESTAMP")) { + stmt.setString(1, key); + stmt.setBinaryStream(2, payload, payloadSize); + stmt.executeUpdate(); + LOGGER.debug( + "External PostgreSQL uploaded key: {}, payload size: {}", key, payloadSize); + } catch (SQLException e) { + String msg = "Error uploading data into External PostgreSQL"; + LOGGER.error(msg, e); + throw new NonTransientException(msg, e); + } + } + + /** + * Downloads the payload stored in the PostgreSQL. + * + * @param key the PostgreSQL key of the object + * @return an input stream containing the contents of the object. Caller is expected to close + * the input stream. + */ + @Override + public InputStream download(String key) { + InputStream inputStream; + try (Connection conn = postgresDataSource.getConnection(); + PreparedStatement stmt = + conn.prepareStatement("SELECT data FROM " + tableName + " WHERE id = ?")) { + stmt.setString(1, key); + try (ResultSet rs = stmt.executeQuery()) { + if (!rs.next()) { + LOGGER.debug("External PostgreSQL data with this ID: {} does not exist", key); + return new ByteArrayInputStream(defaultMessageToUser.getBytes()); + } + inputStream = rs.getBinaryStream(1); + LOGGER.debug("External PostgreSQL downloaded key: {}", key); + } + } catch (SQLException e) { + String msg = "Error downloading data from external PostgreSQL"; + LOGGER.error(msg, e); + throw new NonTransientException(msg, e); + } + return inputStream; + } +} diff --git a/postgres-external-storage/src/main/resources/db/migration_external_postgres/R__initial_schema.sql b/postgres-external-storage/src/main/resources/db/migration_external_postgres/R__initial_schema.sql new file mode 100644 index 000000000..0d0d20dfa --- /dev/null +++ b/postgres-external-storage/src/main/resources/db/migration_external_postgres/R__initial_schema.sql @@ -0,0 +1,56 @@ +-- +-- Copyright 2022 Netflix, Inc. +-- +-- 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. +-- + + +-- -------------------------------------------------------------------------------------------------------------- +-- SCHEMA FOR EXTERNAL PAYLOAD POSTGRES STORAGE +-- -------------------------------------------------------------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS ${tableName} +( + id TEXT, + data bytea NOT NULL, + created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +ALTER TABLE ${tableName} ALTER COLUMN data SET STORAGE EXTERNAL; + +-- Delete trigger to delete the oldest external_payload rows, +-- when there are too many or there are too old. + +DROP TRIGGER IF EXISTS tr_keep_row_number_steady ON ${tableName}; + +CREATE OR REPLACE FUNCTION keep_row_number_steady() + RETURNS TRIGGER AS +$body$ +DECLARE + time_interval interval := concat(${maxDataYears},' years ',${maxDataMonths},' mons ',${maxDataDays},' days' ); +BEGIN + WHILE ((SELECT count(id) FROM ${tableName}) > ${maxDataRows}) OR + ((SELECT min(created_on) FROM ${tableName}) < (CURRENT_TIMESTAMP - time_interval)) + LOOP + DELETE FROM ${tableName} + WHERE created_on = (SELECT min(created_on) FROM ${tableName}); + END LOOP; + RETURN NULL; +END; +$body$ + LANGUAGE plpgsql; + +CREATE TRIGGER tr_keep_row_number_steady + AFTER INSERT ON ${tableName} + FOR EACH ROW EXECUTE PROCEDURE keep_row_number_steady(); \ No newline at end of file diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java new file mode 100644 index 000000000..bea89e224 --- /dev/null +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java @@ -0,0 +1,58 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.controller; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.ResponseEntity; + +import com.netflix.conductor.postgres.storage.PostgresPayloadStorage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ExternalPostgresPayloadResourceTest { + + private PostgresPayloadStorage mockPayloadStorage; + private ExternalPostgresPayloadResource postgresResource; + + @Before + public void before() { + this.mockPayloadStorage = mock(PostgresPayloadStorage.class); + this.postgresResource = new ExternalPostgresPayloadResource(this.mockPayloadStorage); + } + + @Test + public void testGetExternalStorageData() throws IOException { + String data = "Dummy data"; + InputStream inputStreamData = + new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); + when(mockPayloadStorage.download(anyString())).thenReturn(inputStreamData); + ResponseEntity response = + postgresResource.getExternalStorageData("dummyKey.json"); + assertNotNull(response.getBody()); + assertEquals( + data, + new String( + response.getBody().getInputStream().readAllBytes(), + StandardCharsets.UTF_8)); + } +} diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java new file mode 100644 index 000000000..976eb5780 --- /dev/null +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java @@ -0,0 +1,299 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.storage; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +import com.netflix.conductor.common.config.TestObjectMapperConfiguration; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; +import com.netflix.conductor.core.utils.IDGenerator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +@ContextConfiguration(classes = {TestObjectMapperConfiguration.class}) +@RunWith(SpringRunner.class) +public class PostgresPayloadStorageTest { + + private PostgresPayloadTestUtil testPostgres; + private PostgresPayloadStorage executionPostgres; + + public PostgreSQLContainer postgreSQLContainer; + + private final String inputString = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry." + + " Lorem Ipsum has been the industry's standard dummy text ever since the 1500s."; + private final String errorMessage = "{\"Error\": \"Data does not exist.\"}"; + private final InputStream inputData; + private final String key = "dummyKey.json"; + + public PostgresPayloadStorageTest() { + inputData = new ByteArrayInputStream(inputString.getBytes(StandardCharsets.UTF_8)); + } + + @Before + public void setup() { + postgreSQLContainer = + new PostgreSQLContainer<>(DockerImageName.parse("postgres")) + .withDatabaseName("conductor"); + postgreSQLContainer.start(); + testPostgres = new PostgresPayloadTestUtil(postgreSQLContainer); + executionPostgres = + new PostgresPayloadStorage( + testPostgres.getTestProperties(), + testPostgres.getDataSource(), + new IDGenerator(), + errorMessage); + } + + @Test + public void testWriteInputStreamToDb() throws IOException, SQLException { + executionPostgres.upload(key, inputData, inputData.available()); + + PreparedStatement stmt = + testPostgres + .getDataSource() + .getConnection() + .prepareStatement( + "SELECT data FROM external.external_payload WHERE id = 'dummyKey.json'"); + ResultSet rs = stmt.executeQuery(); + rs.next(); + assertEquals( + inputString, + new String(rs.getBinaryStream(1).readAllBytes(), StandardCharsets.UTF_8)); + } + + @Test + public void testReadInputStreamFromDb() throws IOException, SQLException { + insertData(); + assertEquals( + inputString, + new String(executionPostgres.download(key).readAllBytes(), StandardCharsets.UTF_8)); + } + + private void insertData() throws SQLException, IOException { + PreparedStatement stmt = + testPostgres + .getDataSource() + .getConnection() + .prepareStatement("INSERT INTO external.external_payload VALUES (?, ?)"); + stmt.setString(1, key); + stmt.setBinaryStream(2, inputData, inputData.available()); + stmt.executeUpdate(); + } + + @Test(timeout = 60 * 1000) + public void testMultithreadDownload() + throws ExecutionException, InterruptedException, SQLException, IOException { + AtomicInteger threadCounter = new AtomicInteger(0); + insertData(); + int numberOfThread = 12; + int taskInThread = 100; + ArrayList> completableFutures = new ArrayList<>(); + Executor executor = Executors.newFixedThreadPool(numberOfThread); + IntStream.range(0, numberOfThread * taskInThread) + .forEach( + i -> + createFutureForDownloadOperation( + threadCounter, completableFutures, executor)); + for (CompletableFuture completableFuture : completableFutures) { + completableFuture.get(); + } + assertCount(1); + assertEquals(numberOfThread * taskInThread, threadCounter.get()); + } + + private void createFutureForDownloadOperation( + AtomicInteger threadCounter, + ArrayList> completableFutures, + Executor executor) { + CompletableFuture objectCompletableFuture = + CompletableFuture.supplyAsync(() -> downloadData(threadCounter), executor); + completableFutures.add(objectCompletableFuture); + } + + private Void downloadData(AtomicInteger threadCounter) { + try { + assertEquals( + inputString, + new String( + executionPostgres.download(key).readAllBytes(), + StandardCharsets.UTF_8)); + threadCounter.getAndIncrement(); + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testReadNonExistentInputStreamFromDb() throws IOException, SQLException { + PreparedStatement stmt = + testPostgres + .getDataSource() + .getConnection() + .prepareStatement("INSERT INTO external.external_payload VALUES (?, ?)"); + stmt.setString(1, key); + stmt.setBinaryStream(2, inputData, inputData.available()); + stmt.executeUpdate(); + + assertEquals( + errorMessage, + new String( + executionPostgres.download("non_existent_key.json").readAllBytes(), + StandardCharsets.UTF_8)); + } + + @Test + public void testMaxRowInTable() throws IOException, SQLException { + executionPostgres.upload(key, inputData, inputData.available()); + executionPostgres.upload("dummyKey2.json", inputData, inputData.available()); + executionPostgres.upload("dummyKey3.json", inputData, inputData.available()); + executionPostgres.upload("dummyKey4.json", inputData, inputData.available()); + executionPostgres.upload("dummyKey5.json", inputData, inputData.available()); + executionPostgres.upload("dummyKey6.json", inputData, inputData.available()); + executionPostgres.upload("dummyKey7.json", inputData, inputData.available()); + + assertCount(5); + } + + @Test(timeout = 60 * 1000) + public void testMultithreadInsert() + throws SQLException, ExecutionException, InterruptedException { + AtomicInteger threadCounter = new AtomicInteger(0); + int numberOfThread = 12; + int taskInThread = 100; + ArrayList> completableFutures = new ArrayList<>(); + Executor executor = Executors.newFixedThreadPool(numberOfThread); + IntStream.range(0, numberOfThread * taskInThread) + .forEach( + i -> + createFutureForUploadOperation( + threadCounter, completableFutures, executor)); + for (CompletableFuture completableFuture : completableFutures) { + completableFuture.get(); + } + assertCount(1); + assertEquals(numberOfThread * taskInThread, threadCounter.get()); + } + + private void createFutureForUploadOperation( + AtomicInteger threadCounter, + ArrayList> completableFutures, + Executor executor) { + CompletableFuture objectCompletableFuture = + CompletableFuture.supplyAsync(() -> uploadData(threadCounter), executor); + completableFutures.add(objectCompletableFuture); + } + + private Void uploadData(AtomicInteger threadCounter) { + try { + uploadData(); + threadCounter.getAndIncrement(); + return null; + } catch (IOException | SQLException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testHashEnsuringNoDuplicates() + throws IOException, SQLException, InterruptedException { + final String createdOn = uploadData(); + Thread.sleep(500); + final String createdOnAfterUpdate = uploadData(); + assertCount(1); + assertNotEquals(createdOnAfterUpdate, createdOn); + } + + private String uploadData() throws SQLException, IOException { + final String location = getKey(inputString); + ByteArrayInputStream inputStream = + new ByteArrayInputStream(inputString.getBytes(StandardCharsets.UTF_8)); + executionPostgres.upload(location, inputStream, inputStream.available()); + return getCreatedOn(location); + } + + @Test + public void testDistinctHashedKey() { + final String location = getKey(inputString); + final String location2 = getKey(inputString); + final String location3 = getKey(inputString + "A"); + + assertNotEquals(location3, location); + assertEquals(location2, location); + } + + private String getKey(String input) { + return executionPostgres + .getLocation( + ExternalPayloadStorage.Operation.READ, + ExternalPayloadStorage.PayloadType.TASK_INPUT, + "", + input.getBytes(StandardCharsets.UTF_8)) + .getUri(); + } + + private void assertCount(int expected) throws SQLException { + try (PreparedStatement stmt = + testPostgres + .getDataSource() + .getConnection() + .prepareStatement( + "SELECT count(id) FROM external.external_payload"); + ResultSet rs = stmt.executeQuery()) { + rs.next(); + assertEquals(expected, rs.getInt(1)); + } + } + + private String getCreatedOn(String key) throws SQLException { + try (Connection conn = testPostgres.getDataSource().getConnection(); + PreparedStatement stmt = + conn.prepareStatement( + "SELECT created_on FROM external.external_payload WHERE id = ?")) { + stmt.setString(1, key); + try (ResultSet rs = stmt.executeQuery()) { + rs.next(); + return rs.getString(1); + } + } + } + + @After + public void teardown() throws SQLException { + testPostgres.getDataSource().getConnection().close(); + } +} diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java new file mode 100644 index 000000000..b31898ccc --- /dev/null +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java @@ -0,0 +1,73 @@ +/* + *

+ * 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 com.netflix.conductor.postgres.storage; + +import java.nio.file.Paths; +import java.util.Map; + +import javax.sql.DataSource; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.configuration.FluentConfiguration; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.testcontainers.containers.PostgreSQLContainer; + +import com.netflix.conductor.postgres.config.PostgresPayloadProperties; + +public class PostgresPayloadTestUtil { + + private final DataSource dataSource; + private final PostgresPayloadProperties properties = new PostgresPayloadProperties(); + + public PostgresPayloadTestUtil(PostgreSQLContainer postgreSQLContainer) { + + this.dataSource = + DataSourceBuilder.create() + .url(postgreSQLContainer.getJdbcUrl()) + .username(postgreSQLContainer.getUsername()) + .password(postgreSQLContainer.getPassword()) + .build(); + flywayMigrate(dataSource); + } + + private void flywayMigrate(DataSource dataSource) { + FluentConfiguration fluentConfiguration = + Flyway.configure() + .schemas("external") + .locations(Paths.get("db/migration_external_postgres").toString()) + .dataSource(dataSource) + .placeholderReplacement(true) + .placeholders( + Map.of( + "tableName", + "external.external_payload", + "maxDataRows", + "5", + "maxDataDays", + "'1'", + "maxDataMonths", + "'1'", + "maxDataYears", + "'1'")); + + Flyway flyway = fluentConfiguration.load(); + flyway.migrate(); + } + + public DataSource getDataSource() { + return dataSource; + } + + public PostgresPayloadProperties getTestProperties() { + return properties; + } +} diff --git a/settings.gradle b/settings.gradle index a29f042d3..8f3093f2f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -69,6 +69,8 @@ include 'mysql-persistence' include 'postgres-persistence' include 'metrics' include 'es7-persistence' +include 'azureblob-storage' +include 'postgres-external-storage' include 'test-harness' From a44c3c9075e2c11d2f7197bc17042e9f13984c89 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 19:08:16 -0800 Subject: [PATCH 040/202] event queues --- amqp/build.gradle | 15 + amqp/dependencies.lock | 177 ++++ .../contribs/queue/amqp/AMQPConnection.java | 391 ++++++++ .../queue/amqp/AMQPObservableQueue.java | 864 +++++++++++++++++ .../config/AMQPEventQueueConfiguration.java | 86 ++ .../amqp/config/AMQPEventQueueProperties.java | 313 ++++++ .../amqp/config/AMQPEventQueueProvider.java | 59 ++ .../queue/amqp/config/AMQPRetryPattern.java | 54 ++ .../queue/amqp/util/AMQPConfigurations.java | 40 + .../queue/amqp/util/AMQPConstants.java | 91 ++ .../queue/amqp/util/AMQPSettings.java | 315 ++++++ .../queue/amqp/util/ConnectionType.java | 18 + .../contribs/queue/amqp/util/RetryType.java | 20 + .../amqp/AMQPEventQueueProviderTest.java | 82 ++ .../queue/amqp/AMQPObservableQueueTest.java | 911 ++++++++++++++++++ .../contribs/queue/amqp/AMQPSettingsTest.java | 89 ++ dependencies.gradle | 3 + nats-streaming/README.md | 46 + nats-streaming/build.gradle | 14 + nats-streaming/dependencies.lock | 189 ++++ .../queue/stan/NATSAbstractQueue.java | 301 ++++++ .../queue/stan/NATSObservableQueue.java | 114 +++ .../queue/stan/NATSStreamObservableQueue.java | 144 +++ .../queue/stan/config/NATSConfiguration.java | 32 + .../stan/config/NATSEventQueueProvider.java | 59 ++ .../stan/config/NATSStreamConfiguration.java | 84 ++ .../config/NATSStreamEventQueueProvider.java | 79 ++ .../stan/config/NATSStreamProperties.java | 76 ++ nats/build.gradle | 13 + nats/dependencies.lock | 177 ++++ .../queue/nats/JetStreamObservableQueue.java | 285 ++++++ .../contribs/queue/nats/JsmMessage.java | 30 + .../queue/nats/NATSAbstractQueue.java | 301 ++++++ .../queue/nats/NATSObservableQueue.java | 114 +++ .../contribs/queue/nats/NatsException.java | 39 + .../nats/config/JetStreamConfiguration.java | 79 ++ .../config/JetStreamEventQueueProvider.java | 57 ++ .../nats/config/JetStreamProperties.java | 88 ++ .../queue/nats/config/NATSConfiguration.java | 32 + .../nats/config/NATSEventQueueProvider.java | 59 ++ settings.gradle | 3 + 41 files changed, 5943 insertions(+) create mode 100644 amqp/build.gradle create mode 100644 amqp/dependencies.lock create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java create mode 100644 amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java create mode 100644 amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java create mode 100644 amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java create mode 100644 amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java create mode 100644 nats-streaming/README.md create mode 100644 nats-streaming/build.gradle create mode 100644 nats-streaming/dependencies.lock create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java create mode 100644 nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java create mode 100644 nats/build.gradle create mode 100644 nats/dependencies.lock create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java create mode 100644 nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java diff --git a/amqp/build.gradle b/amqp/build.gradle new file mode 100644 index 000000000..3d41fb3e5 --- /dev/null +++ b/amqp/build.gradle @@ -0,0 +1,15 @@ +dependencies { + implementation project(':conductor-common') + implementation project(':conductor-core') + + implementation "com.rabbitmq:amqp-client:${revAmqpClient}" + implementation "org.apache.commons:commons-lang3:" + implementation "com.google.guava:guava:${revGuava}" + implementation "io.reactivex:rxjava:${revRxJava}" + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + + + +} \ No newline at end of file diff --git a/amqp/dependencies.lock b/amqp/dependencies.lock new file mode 100644 index 000000000..f9f37598f --- /dev/null +++ b/amqp/dependencies.lock @@ -0,0 +1,177 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + }, + "testRuntimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "com.rabbitmq:amqp-client": { + "locked": "5.13.0" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + } +} \ No newline at end of file diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java new file mode 100644 index 000000000..c308d1593 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java @@ -0,0 +1,391 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPRetryPattern; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPConstants; +import com.netflix.conductor.contribs.queue.amqp.util.ConnectionType; + +import com.rabbitmq.client.Address; +import com.rabbitmq.client.BlockedListener; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.ShutdownListener; +import com.rabbitmq.client.ShutdownSignalException; + +public class AMQPConnection { + + private static Logger LOGGER = LoggerFactory.getLogger(AMQPConnection.class); + private volatile Connection publisherConnection = null; + private volatile Connection subscriberConnection = null; + private ConnectionFactory factory = null; + private Address[] addresses = null; + private static AMQPConnection amqpConnection = null; + private static final String PUBLISHER = "Publisher"; + private static final String SUBSCRIBER = "Subscriber"; + private static final Map> availableChannelPool = + new ConcurrentHashMap>(); + private static final Map subscriberReservedChannelPool = + new ConcurrentHashMap(); + private static AMQPRetryPattern retrySettings = null; + + private AMQPConnection() {} + + private AMQPConnection(final ConnectionFactory factory, final Address[] address) { + this.factory = factory; + this.addresses = address; + } + + public static synchronized AMQPConnection getInstance( + final ConnectionFactory factory, + final Address[] address, + final AMQPRetryPattern retrySettings) { + if (AMQPConnection.amqpConnection == null) { + AMQPConnection.amqpConnection = new AMQPConnection(factory, address); + } + AMQPConnection.retrySettings = retrySettings; + return AMQPConnection.amqpConnection; + } + + // Exposed for UT + public static void setAMQPConnection(AMQPConnection amqpConnection) { + AMQPConnection.amqpConnection = amqpConnection; + } + + public Address[] getAddresses() { + return addresses; + } + + private Connection createConnection(String connectionPrefix) { + int retryIndex = 1; + while (true) { + try { + Connection connection = + factory.newConnection( + addresses, System.getenv("HOSTNAME") + "-" + connectionPrefix); + if (connection == null || !connection.isOpen()) { + throw new RuntimeException("Failed to open connection"); + } + connection.addShutdownListener( + new ShutdownListener() { + @Override + public void shutdownCompleted(ShutdownSignalException cause) { + LOGGER.error( + "Received a shutdown exception for the connection {}. reason {} cause{}", + connection.getClientProvidedName(), + cause.getMessage(), + cause); + } + }); + connection.addBlockedListener( + new BlockedListener() { + @Override + public void handleUnblocked() throws IOException { + LOGGER.info( + "Connection {} is unblocked", + connection.getClientProvidedName()); + } + + @Override + public void handleBlocked(String reason) throws IOException { + LOGGER.error( + "Connection {} is blocked. reason: {}", + connection.getClientProvidedName(), + reason); + } + }); + return connection; + } catch (final IOException e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + final String error = + "IO error while connecting to " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")); + LOGGER.error(error, e); + throw new RuntimeException(error, e); + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + final String error = + "Retries completed. IO error while connecting to " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")); + LOGGER.error(error, e); + throw new RuntimeException(error, e); + } + retryIndex++; + } catch (final TimeoutException e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + final String error = + "Timeout while connecting to " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")); + LOGGER.error(error, e); + throw new RuntimeException(error, e); + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + final String error = + "Retries completed. Timeout while connecting to " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")); + LOGGER.error(error, e); + throw new RuntimeException(error, e); + } + retryIndex++; + } + } + } + + public Channel getOrCreateChannel(ConnectionType connectionType, String queueOrExchangeName) + throws Exception { + LOGGER.debug( + "Accessing the channel for queueOrExchange {} with type {} ", + queueOrExchangeName, + connectionType); + switch (connectionType) { + case SUBSCRIBER: + String subChnName = connectionType + ";" + queueOrExchangeName; + if (subscriberReservedChannelPool.containsKey(subChnName)) { + Channel locChn = subscriberReservedChannelPool.get(subChnName); + if (locChn != null && locChn.isOpen()) { + return locChn; + } + } + synchronized (this) { + if (subscriberConnection == null || !subscriberConnection.isOpen()) { + subscriberConnection = createConnection(SUBSCRIBER); + } + } + Channel subChn = borrowChannel(connectionType, subscriberConnection); + // Add the subscribed channels to Map to avoid messages being acknowledged on + // different from the subscribed one + subscriberReservedChannelPool.put(subChnName, subChn); + return subChn; + case PUBLISHER: + synchronized (this) { + if (publisherConnection == null || !publisherConnection.isOpen()) { + publisherConnection = createConnection(PUBLISHER); + } + } + return borrowChannel(connectionType, publisherConnection); + default: + return null; + } + } + + private Channel getOrCreateChannel(ConnectionType connType, Connection rmqConnection) { + // Channel creation is required + Channel locChn = null; + int retryIndex = 1; + while (true) { + try { + LOGGER.debug("Creating a channel for " + connType); + locChn = rmqConnection.createChannel(); + if (locChn == null || !locChn.isOpen()) { + throw new RuntimeException("Fail to open " + connType + " channel"); + } + locChn.addShutdownListener( + cause -> { + LOGGER.error( + connType + " Channel has been shutdown: {}", + cause.getMessage(), + cause); + }); + return locChn; + } catch (final IOException e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + throw new RuntimeException( + "Cannot open " + + connType + + " channel on " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")), + e); + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + throw new RuntimeException( + "Retries completed. Cannot open " + + connType + + " channel on " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")), + e); + } + retryIndex++; + } catch (final Exception e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + throw new RuntimeException( + "Cannot open " + + connType + + " channel on " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")), + e); + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + throw new RuntimeException( + "Retries completed. Cannot open " + + connType + + " channel on " + + Arrays.stream(addresses) + .map(address -> address.toString()) + .collect(Collectors.joining(",")), + e); + } + retryIndex++; + } + } + } + + public void close() { + LOGGER.info("Closing all connections and channels"); + try { + closeChannelsInMap(ConnectionType.PUBLISHER); + closeChannelsInMap(ConnectionType.SUBSCRIBER); + closeConnection(publisherConnection); + closeConnection(subscriberConnection); + } finally { + availableChannelPool.clear(); + publisherConnection = null; + subscriberConnection = null; + } + } + + private void closeChannelsInMap(ConnectionType conType) { + Set channels = availableChannelPool.get(conType); + if (channels != null && !channels.isEmpty()) { + Iterator itr = channels.iterator(); + while (itr.hasNext()) { + Channel channel = itr.next(); + closeChannel(channel); + } + channels.clear(); + } + } + + private void closeConnection(Connection connection) { + if (connection == null || !connection.isOpen()) { + LOGGER.warn("Connection is null or closed already. Not closing it again"); + } else { + try { + connection.close(); + } catch (Exception e) { + LOGGER.warn("Fail to close connection: {}", e.getMessage(), e); + } + } + } + + private void closeChannel(Channel channel) { + if (channel == null || !channel.isOpen()) { + LOGGER.warn("Channel is null or closed already. Not closing it again"); + } else { + try { + channel.close(); + } catch (Exception e) { + LOGGER.warn("Fail to close channel: {}", e.getMessage(), e); + } + } + } + + /** + * Gets the channel for specified connectionType. + * + * @param connectionType holds the multiple channels for different connection types for thread + * safe operation. + * @param rmqConnection publisher or subscriber connection instance + * @return channel instance + * @throws Exception + */ + private synchronized Channel borrowChannel( + ConnectionType connectionType, Connection rmqConnection) throws Exception { + if (!availableChannelPool.containsKey(connectionType)) { + Channel channel = getOrCreateChannel(connectionType, rmqConnection); + LOGGER.info(String.format(AMQPConstants.INFO_CHANNEL_CREATION_SUCCESS, connectionType)); + return channel; + } + Set channels = availableChannelPool.get(connectionType); + if (channels != null && channels.isEmpty()) { + Channel channel = getOrCreateChannel(connectionType, rmqConnection); + LOGGER.info(String.format(AMQPConstants.INFO_CHANNEL_CREATION_SUCCESS, connectionType)); + return channel; + } + Iterator itr = channels.iterator(); + while (itr.hasNext()) { + Channel channel = itr.next(); + if (channel != null && channel.isOpen()) { + itr.remove(); + LOGGER.info( + String.format(AMQPConstants.INFO_CHANNEL_BORROW_SUCCESS, connectionType)); + return channel; + } else { + itr.remove(); + } + } + Channel channel = getOrCreateChannel(connectionType, rmqConnection); + LOGGER.info(String.format(AMQPConstants.INFO_CHANNEL_RESET_SUCCESS, connectionType)); + return channel; + } + + /** + * Returns the channel to connection pool for specified connectionType. + * + * @param connectionType + * @param channel + * @throws Exception + */ + public synchronized void returnChannel(ConnectionType connectionType, Channel channel) + throws Exception { + if (channel == null || !channel.isOpen()) { + channel = null; // channel is reset. + } + Set channels = availableChannelPool.get(connectionType); + if (channels == null) { + channels = new HashSet(); + availableChannelPool.put(connectionType, channels); + } + channels.add(channel); + LOGGER.info(String.format(AMQPConstants.INFO_CHANNEL_RETURN_SUCCESS, connectionType)); + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java new file mode 100644 index 000000000..15d33f09d --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java @@ -0,0 +1,864 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProperties; +import com.netflix.conductor.contribs.queue.amqp.config.AMQPRetryPattern; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPConstants; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPSettings; +import com.netflix.conductor.contribs.queue.amqp.util.ConnectionType; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.events.queue.ObservableQueue; +import com.netflix.conductor.metrics.Monitors; + +import com.google.common.collect.Maps; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Address; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.Consumer; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; +import com.rabbitmq.client.GetResponse; +import rx.Observable; +import rx.Subscriber; + +/** + * @author Ritu Parathody + */ +public class AMQPObservableQueue implements ObservableQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(AMQPObservableQueue.class); + + private final AMQPSettings settings; + private final AMQPRetryPattern retrySettings; + private final String QUEUE_TYPE = "x-queue-type"; + private final int batchSize; + private final boolean useExchange; + private int pollTimeInMS; + private AMQPConnection amqpConnection; + + protected LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + private volatile boolean running; + + public AMQPObservableQueue( + ConnectionFactory factory, + Address[] addresses, + boolean useExchange, + AMQPSettings settings, + AMQPRetryPattern retrySettings, + int batchSize, + int pollTimeInMS) { + if (factory == null) { + throw new IllegalArgumentException("Connection factory is undefined"); + } + if (addresses == null || addresses.length == 0) { + throw new IllegalArgumentException("Addresses are undefined"); + } + if (settings == null) { + throw new IllegalArgumentException("Settings are undefined"); + } + if (batchSize <= 0) { + throw new IllegalArgumentException("Batch size must be greater than 0"); + } + if (pollTimeInMS <= 0) { + throw new IllegalArgumentException("Poll time must be greater than 0 ms"); + } + this.useExchange = useExchange; + this.settings = settings; + this.batchSize = batchSize; + this.amqpConnection = AMQPConnection.getInstance(factory, addresses, retrySettings); + this.retrySettings = retrySettings; + this.setPollTimeInMS(pollTimeInMS); + } + + @Override + public Observable observe() { + Observable.OnSubscribe onSubscribe = null; + // This will enabled the messages to be processed one after the other as per the + // observable next behavior. + if (settings.isSequentialProcessing()) { + LOGGER.info("Subscribing for the message processing on schedule basis"); + receiveMessages(); + onSubscribe = + subscriber -> { + Observable interval = + Observable.interval(pollTimeInMS, TimeUnit.MILLISECONDS); + interval.flatMap( + (Long x) -> { + if (!isRunning()) { + LOGGER.debug( + "Component stopped, skip listening for messages from RabbitMQ"); + return Observable.from(Collections.emptyList()); + } else { + List available = new LinkedList<>(); + messages.drainTo(available); + + if (!available.isEmpty()) { + AtomicInteger count = new AtomicInteger(0); + StringBuilder buffer = new StringBuilder(); + available.forEach( + msg -> { + buffer.append(msg.getId()) + .append("=") + .append(msg.getPayload()); + count.incrementAndGet(); + + if (count.get() + < available.size()) { + buffer.append(","); + } + }); + LOGGER.info( + String.format( + "Batch from %s to conductor is %s", + settings + .getQueueOrExchangeName(), + buffer.toString())); + } + return Observable.from(available); + } + }) + .subscribe(subscriber::onNext, subscriber::onError); + }; + LOGGER.info("Subscribed for the message processing on schedule basis"); + } else { + onSubscribe = + subscriber -> { + LOGGER.info("Subscribing for the event based AMQP message processing"); + receiveMessages(subscriber); + LOGGER.info("Subscribed for the event based AMQP message processing"); + }; + } + return Observable.create(onSubscribe); + } + + @Override + public String getType() { + return useExchange ? AMQPConstants.AMQP_EXCHANGE_TYPE : AMQPConstants.AMQP_QUEUE_TYPE; + } + + @Override + public String getName() { + return settings.getEventName(); + } + + @Override + public String getURI() { + return settings.getQueueOrExchangeName(); + } + + public int getBatchSize() { + return batchSize; + } + + public AMQPSettings getSettings() { + return settings; + } + + public Address[] getAddresses() { + return amqpConnection.getAddresses(); + } + + public List ack(List messages) { + final List failedMessages = new ArrayList<>(); + for (final Message message : messages) { + try { + ackMsg(message); + } catch (final Exception e) { + LOGGER.error("Cannot ACK message with delivery tag {}", message.getReceipt(), e); + failedMessages.add(message.getReceipt()); + } + } + return failedMessages; + } + + public void ackMsg(Message message) throws Exception { + int retryIndex = 1; + while (true) { + try { + LOGGER.info("ACK message with delivery tag {}", message.getReceipt()); + Channel chn = + amqpConnection.getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()); + chn.basicAck(Long.parseLong(message.getReceipt()), false); + LOGGER.info("Ack'ed the message with delivery tag {}", message.getReceipt()); + break; + } catch (final Exception e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + LOGGER.error( + "Cannot ACK message with delivery tag {}", message.getReceipt(), e); + throw e; + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + LOGGER.error( + "Retries completed. Cannot ACK message with delivery tag {}", + message.getReceipt(), + e); + throw ex; + } + retryIndex++; + } + } + } + + @Override + public void nack(List messages) { + for (final Message message : messages) { + int retryIndex = 1; + while (true) { + try { + LOGGER.info("NACK message with delivery tag {}", message.getReceipt()); + Channel chn = + amqpConnection.getOrCreateChannel( + ConnectionType.SUBSCRIBER, + getSettings().getQueueOrExchangeName()); + chn.basicNack(Long.parseLong(message.getReceipt()), false, false); + LOGGER.info("Nack'ed the message with delivery tag {}", message.getReceipt()); + break; + } catch (final Exception e) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + LOGGER.error( + "Cannot NACK message with delivery tag {}", + message.getReceipt(), + e); + } + try { + retry.continueOrPropogate(e, retryIndex); + } catch (Exception ex) { + LOGGER.error( + "Retries completed. Cannot NACK message with delivery tag {}", + message.getReceipt(), + e); + break; + } + retryIndex++; + } + } + } + } + + private static AMQP.BasicProperties buildBasicProperties( + final Message message, final AMQPSettings settings) { + return new AMQP.BasicProperties.Builder() + .messageId( + StringUtils.isEmpty(message.getId()) + ? UUID.randomUUID().toString() + : message.getId()) + .correlationId( + StringUtils.isEmpty(message.getReceipt()) + ? UUID.randomUUID().toString() + : message.getReceipt()) + .contentType(settings.getContentType()) + .contentEncoding(settings.getContentEncoding()) + .deliveryMode(settings.getDeliveryMode()) + .build(); + } + + private void publishMessage(Message message, String exchange, String routingKey) { + Channel chn = null; + int retryIndex = 1; + while (true) { + try { + final String payload = message.getPayload(); + chn = + amqpConnection.getOrCreateChannel( + ConnectionType.PUBLISHER, getSettings().getQueueOrExchangeName()); + chn.basicPublish( + exchange, + routingKey, + buildBasicProperties(message, settings), + payload.getBytes(settings.getContentEncoding())); + LOGGER.info(String.format("Published message to %s: %s", exchange, payload)); + break; + } catch (Exception ex) { + AMQPRetryPattern retry = retrySettings; + if (retry == null) { + LOGGER.error( + "Failed to publish message {} to {}", + message.getPayload(), + exchange, + ex); + throw new RuntimeException(ex); + } + try { + retry.continueOrPropogate(ex, retryIndex); + } catch (Exception e) { + LOGGER.error( + "Retries completed. Failed to publish message {} to {}", + message.getPayload(), + exchange, + ex); + throw new RuntimeException(ex); + } + retryIndex++; + } finally { + if (chn != null) { + try { + amqpConnection.returnChannel(ConnectionType.PUBLISHER, chn); + } catch (Exception e) { + LOGGER.error( + "Failed to return the channel of {}. {}", + ConnectionType.PUBLISHER, + e); + } + } + } + } + } + + @Override + public void publish(List messages) { + try { + final String exchange, routingKey; + if (useExchange) { + // Use exchange + routing key for publishing + getOrCreateExchange( + ConnectionType.PUBLISHER, + settings.getQueueOrExchangeName(), + settings.getExchangeType(), + settings.isDurable(), + settings.autoDelete(), + settings.getArguments()); + exchange = settings.getQueueOrExchangeName(); + routingKey = settings.getRoutingKey(); + } else { + // Use queue for publishing + final AMQP.Queue.DeclareOk declareOk = + getOrCreateQueue( + ConnectionType.PUBLISHER, + settings.getQueueOrExchangeName(), + settings.isDurable(), + settings.isExclusive(), + settings.autoDelete(), + settings.getArguments()); + exchange = StringUtils.EMPTY; // Empty exchange name for queue + routingKey = declareOk.getQueue(); // Routing name is the name of queue + } + messages.forEach(message -> publishMessage(message, exchange, routingKey)); + } catch (final RuntimeException ex) { + throw ex; + } catch (final Exception ex) { + LOGGER.error("Failed to publish messages: {}", ex.getMessage(), ex); + throw new RuntimeException(ex); + } + } + + @Override + public void setUnackTimeout(Message message, long unackTimeout) { + throw new UnsupportedOperationException(); + } + + @Override + public long size() { + Channel chn = null; + try { + chn = + amqpConnection.getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()); + return chn.messageCount(settings.getQueueOrExchangeName()); + } catch (final Exception e) { + throw new RuntimeException(e); + } finally { + if (chn != null) { + try { + amqpConnection.returnChannel(ConnectionType.SUBSCRIBER, chn); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + @Override + public void close() { + amqpConnection.close(); + } + + @Override + public void start() { + LOGGER.info( + "Started listening to {}:{}", + getClass().getSimpleName(), + settings.getQueueOrExchangeName()); + running = true; + } + + @Override + public void stop() { + LOGGER.info( + "Stopped listening to {}:{}", + getClass().getSimpleName(), + settings.getQueueOrExchangeName()); + running = false; + } + + @Override + public boolean isRunning() { + return running; + } + + public static class Builder { + + private final Address[] addresses; + private final int batchSize; + private final int pollTimeInMS; + private final ConnectionFactory factory; + private final AMQPEventQueueProperties properties; + + public Builder(AMQPEventQueueProperties properties) { + this.properties = properties; + this.addresses = buildAddressesFromHosts(); + this.factory = buildConnectionFactory(); + // messages polling settings + this.batchSize = properties.getBatchSize(); + this.pollTimeInMS = (int) properties.getPollTimeDuration().toMillis(); + } + + private Address[] buildAddressesFromHosts() { + // Read hosts from config + final String hosts = properties.getHosts(); + if (StringUtils.isEmpty(hosts)) { + throw new IllegalArgumentException("Hosts are undefined"); + } + return Address.parseAddresses(hosts); + } + + private ConnectionFactory buildConnectionFactory() { + final ConnectionFactory factory = new ConnectionFactory(); + // Get rabbitmq username from config + final String username = properties.getUsername(); + if (StringUtils.isEmpty(username)) { + throw new IllegalArgumentException("Username is null or empty"); + } else { + factory.setUsername(username); + } + // Get rabbitmq password from config + final String password = properties.getPassword(); + if (StringUtils.isEmpty(password)) { + throw new IllegalArgumentException("Password is null or empty"); + } else { + factory.setPassword(password); + } + // Get vHost from config + final String virtualHost = properties.getVirtualHost(); + ; + if (StringUtils.isEmpty(virtualHost)) { + throw new IllegalArgumentException("Virtual host is null or empty"); + } else { + factory.setVirtualHost(virtualHost); + } + // Get server port from config + final int port = properties.getPort(); + if (port <= 0) { + throw new IllegalArgumentException("Port must be greater than 0"); + } else { + factory.setPort(port); + } + final boolean useNio = properties.isUseNio(); + if (useNio) { + factory.useNio(); + } + final boolean useSslProtocol = properties.isUseSslProtocol(); + if (useSslProtocol) { + try { + factory.useSslProtocol(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IllegalArgumentException("Invalid sslProtocol ", e); + } + } + factory.setConnectionTimeout(properties.getConnectionTimeoutInMilliSecs()); + factory.setRequestedHeartbeat(properties.getRequestHeartbeatTimeoutInSecs()); + factory.setNetworkRecoveryInterval(properties.getNetworkRecoveryIntervalInMilliSecs()); + factory.setHandshakeTimeout(properties.getHandshakeTimeoutInMilliSecs()); + factory.setAutomaticRecoveryEnabled(true); + factory.setTopologyRecoveryEnabled(true); + factory.setRequestedChannelMax(properties.getMaxChannelCount()); + return factory; + } + + public AMQPObservableQueue build(final boolean useExchange, final String queueURI) { + final AMQPSettings settings = new AMQPSettings(properties).fromURI(queueURI); + final AMQPRetryPattern retrySettings = + new AMQPRetryPattern( + properties.getLimit(), properties.getDuration(), properties.getType()); + return new AMQPObservableQueue( + factory, + addresses, + useExchange, + settings, + retrySettings, + batchSize, + pollTimeInMS); + } + } + + private AMQP.Exchange.DeclareOk getOrCreateExchange(ConnectionType connectionType) + throws Exception { + return getOrCreateExchange( + connectionType, + settings.getQueueOrExchangeName(), + settings.getExchangeType(), + settings.isDurable(), + settings.autoDelete(), + settings.getArguments()); + } + + private AMQP.Exchange.DeclareOk getOrCreateExchange( + ConnectionType connectionType, + String name, + final String type, + final boolean isDurable, + final boolean autoDelete, + final Map arguments) + throws Exception { + if (StringUtils.isEmpty(name)) { + throw new RuntimeException("Exchange name is undefined"); + } + if (StringUtils.isEmpty(type)) { + throw new RuntimeException("Exchange type is undefined"); + } + Channel chn = null; + try { + LOGGER.debug("Creating exchange {} of type {}", name, type); + chn = + amqpConnection.getOrCreateChannel( + connectionType, getSettings().getQueueOrExchangeName()); + return chn.exchangeDeclare(name, type, isDurable, autoDelete, arguments); + } catch (final Exception e) { + LOGGER.warn("Failed to create exchange {} of type {}", name, type, e); + throw e; + } finally { + if (chn != null) { + try { + amqpConnection.returnChannel(connectionType, chn); + } catch (Exception e) { + LOGGER.error("Failed to return the channel of {}. {}", connectionType, e); + } + } + } + } + + private AMQP.Queue.DeclareOk getOrCreateQueue(ConnectionType connectionType) throws Exception { + return getOrCreateQueue( + connectionType, + settings.getQueueOrExchangeName(), + settings.isDurable(), + settings.isExclusive(), + settings.autoDelete(), + settings.getArguments()); + } + + private AMQP.Queue.DeclareOk getOrCreateQueue( + ConnectionType connectionType, + final String name, + final boolean isDurable, + final boolean isExclusive, + final boolean autoDelete, + final Map arguments) + throws Exception { + if (StringUtils.isEmpty(name)) { + throw new RuntimeException("Queue name is undefined"); + } + arguments.put(QUEUE_TYPE, settings.getQueueType()); + Channel chn = null; + try { + LOGGER.debug("Creating queue {}", name); + chn = + amqpConnection.getOrCreateChannel( + connectionType, getSettings().getQueueOrExchangeName()); + return chn.queueDeclare(name, isDurable, isExclusive, autoDelete, arguments); + } catch (final Exception e) { + LOGGER.warn("Failed to create queue {}", name, e); + throw e; + } finally { + if (chn != null) { + try { + amqpConnection.returnChannel(connectionType, chn); + } catch (Exception e) { + LOGGER.error("Failed to return the channel of {}. {}", connectionType, e); + } + } + } + } + + private static Message asMessage(AMQPSettings settings, GetResponse response) throws Exception { + if (response == null) { + return null; + } + final Message message = new Message(); + message.setId(response.getProps().getMessageId()); + message.setPayload(new String(response.getBody(), settings.getContentEncoding())); + message.setReceipt(String.valueOf(response.getEnvelope().getDeliveryTag())); + return message; + } + + private void receiveMessagesFromQueue(String queueName) throws Exception { + LOGGER.debug("Accessing channel for queue {}", queueName); + + Consumer consumer = + new DefaultConsumer( + amqpConnection.getOrCreateChannel( + ConnectionType.SUBSCRIBER, + getSettings().getQueueOrExchangeName())) { + + @Override + public void handleDelivery( + final String consumerTag, + final Envelope envelope, + final AMQP.BasicProperties properties, + final byte[] body) + throws IOException { + try { + Message message = + asMessage( + settings, + new GetResponse( + envelope, properties, body, Integer.MAX_VALUE)); + if (message != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug( + "Got message with ID {} and receipt {}", + message.getId(), + message.getReceipt()); + } + messages.add(message); + LOGGER.info("receiveMessagesFromQueue- End method {}", messages); + } + } catch (InterruptedException e) { + LOGGER.error( + "Issue in handling the mesages for the subscriber with consumer tag {}. {}", + consumerTag, + e); + Thread.currentThread().interrupt(); + } catch (Exception e) { + LOGGER.error( + "Issue in handling the mesages for the subscriber with consumer tag {}. {}", + consumerTag, + e); + } + } + + public void handleCancel(String consumerTag) throws IOException { + LOGGER.error( + "Recieved a consumer cancel notification for subscriber {}", + consumerTag); + } + }; + + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()) + .basicConsume(queueName, false, consumer); + Monitors.recordEventQueueMessagesProcessed(getType(), queueName, messages.size()); + } + + private void receiveMessagesFromQueue(String queueName, Subscriber subscriber) + throws Exception { + LOGGER.debug("Accessing channel for queue {}", queueName); + + Consumer consumer = + new DefaultConsumer( + amqpConnection.getOrCreateChannel( + ConnectionType.SUBSCRIBER, + getSettings().getQueueOrExchangeName())) { + + @Override + public void handleDelivery( + final String consumerTag, + final Envelope envelope, + final AMQP.BasicProperties properties, + final byte[] body) + throws IOException { + try { + Message message = + asMessage( + settings, + new GetResponse( + envelope, properties, body, Integer.MAX_VALUE)); + if (message == null) { + return; + } + LOGGER.info( + "Got message with ID {} and receipt {}", + message.getId(), + message.getReceipt()); + LOGGER.debug("Message content {}", message); + // Not using thread-pool here as the number of concurrent threads are + // controlled + // by the number of messages delivery using pre-fetch count in RabbitMQ + Thread newThread = + new Thread( + () -> { + LOGGER.info( + "Spawning a new thread for message with ID {}", + message.getId()); + subscriber.onNext(message); + }); + newThread.start(); + } catch (InterruptedException e) { + LOGGER.error( + "Issue in handling the mesages for the subscriber with consumer tag {}. {}", + consumerTag, + e); + Thread.currentThread().interrupt(); + } catch (Exception e) { + LOGGER.error( + "Issue in handling the mesages for the subscriber with consumer tag {}. {}", + consumerTag, + e); + } + } + + public void handleCancel(String consumerTag) throws IOException { + LOGGER.error( + "Recieved a consumer cancel notification for subscriber {}", + consumerTag); + } + }; + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()) + .basicConsume(queueName, false, consumer); + } + + protected void receiveMessages() { + try { + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()) + .basicQos(batchSize); + String queueName; + if (useExchange) { + // Consume messages from an exchange + getOrCreateExchange(ConnectionType.SUBSCRIBER); + /* + * Create queue if not present based on the settings provided in the queue URI + * or configuration properties. Sample URI format: + * amqp_exchange:myExchange?bindQueueName=myQueue&exchangeType=topic&routingKey=myRoutingKey&exclusive + * =false&autoDelete=false&durable=true Default settings if not provided in the + * queue URI or properties: isDurable: true, autoDelete: false, isExclusive: + * false The same settings are currently used during creation of exchange as + * well as queue. TODO: This can be enhanced further to get the settings + * separately for exchange and queue from the URI + */ + final AMQP.Queue.DeclareOk declareOk = + getOrCreateQueue( + ConnectionType.SUBSCRIBER, + settings.getExchangeBoundQueueName(), + settings.isDurable(), + settings.isExclusive(), + settings.autoDelete(), + Maps.newHashMap()); + // Bind the declared queue to exchange + queueName = declareOk.getQueue(); + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()) + .queueBind( + queueName, + settings.getQueueOrExchangeName(), + settings.getRoutingKey()); + } else { + // Consume messages from a queue + queueName = getOrCreateQueue(ConnectionType.SUBSCRIBER).getQueue(); + } + // Consume messages + LOGGER.info("Consuming from queue {}", queueName); + receiveMessagesFromQueue(queueName); + } catch (Exception exception) { + LOGGER.error("Exception while getting messages from RabbitMQ", exception); + Monitors.recordObservableQMessageReceivedErrors(getType()); + } + } + + protected void receiveMessages(Subscriber subscriber) { + try { + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, getSettings().getQueueOrExchangeName()) + .basicQos(batchSize); + String queueName; + if (useExchange) { + // Consume messages from an exchange + getOrCreateExchange(ConnectionType.SUBSCRIBER); + /* + * Create queue if not present based on the settings provided in the queue URI + * or configuration properties. Sample URI format: + * amqp_exchange:myExchange?bindQueueName=myQueue&exchangeType=topic&routingKey=myRoutingKey&exclusive + * =false&autoDelete=false&durable=true Default settings if not provided in the + * queue URI or properties: isDurable: true, autoDelete: false, isExclusive: + * false The same settings are currently used during creation of exchange as + * well as queue. TODO: This can be enhanced further to get the settings + * separately for exchange and queue from the URI + */ + final AMQP.Queue.DeclareOk declareOk = + getOrCreateQueue( + ConnectionType.SUBSCRIBER, + settings.getExchangeBoundQueueName(), + settings.isDurable(), + settings.isExclusive(), + settings.autoDelete(), + Maps.newHashMap()); + // Bind the declared queue to exchange + queueName = declareOk.getQueue(); + amqpConnection + .getOrCreateChannel( + ConnectionType.SUBSCRIBER, settings.getQueueOrExchangeName()) + .queueBind( + queueName, + settings.getQueueOrExchangeName(), + settings.getRoutingKey()); + } else { + // Consume messages from a queue + queueName = getOrCreateQueue(ConnectionType.SUBSCRIBER).getQueue(); + } + // Consume messages + LOGGER.info("Consuming from queue {}", queueName); + receiveMessagesFromQueue(queueName, subscriber); + } catch (Exception exception) { + LOGGER.error("Exception while getting messages from RabbitMQ", exception); + Monitors.recordObservableQMessageReceivedErrors(getType()); + } + } + + public int getPollTimeInMS() { + return pollTimeInMS; + } + + public void setPollTimeInMS(int pollTimeInMS) { + this.pollTimeInMS = pollTimeInMS; + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java new file mode 100644 index 000000000..b23b64fe0 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java @@ -0,0 +1,86 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.config; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.contribs.queue.amqp.AMQPObservableQueue.Builder; +import com.netflix.conductor.core.config.ConductorProperties; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; +import com.netflix.conductor.model.TaskModel.Status; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(AMQPEventQueueProperties.class) +@ConditionalOnProperty(name = "conductor.event-queues.amqp.enabled", havingValue = "true") +public class AMQPEventQueueConfiguration { + + private enum QUEUE_TYPE { + AMQP_QUEUE("amqp_queue"), + AMQP_EXCHANGE("amqp_exchange"); + + private final String type; + + QUEUE_TYPE(String type) { + this.type = type; + } + + public String getType() { + return type; + } + } + + @Bean + public EventQueueProvider amqpEventQueueProvider(AMQPEventQueueProperties properties) { + return new AMQPEventQueueProvider(properties, QUEUE_TYPE.AMQP_QUEUE.getType(), false); + } + + @Bean + public EventQueueProvider amqpExchangeEventQueueProvider(AMQPEventQueueProperties properties) { + return new AMQPEventQueueProvider(properties, QUEUE_TYPE.AMQP_EXCHANGE.getType(), true); + } + + @ConditionalOnProperty(name = "conductor.default-event-queue.type", havingValue = "amqp") + @Bean + public Map getQueues( + ConductorProperties conductorProperties, AMQPEventQueueProperties properties) { + String stack = ""; + if (conductorProperties.getStack() != null && conductorProperties.getStack().length() > 0) { + stack = conductorProperties.getStack() + "_"; + } + final boolean useExchange = properties.isUseExchange(); + + Status[] statuses = new Status[] {Status.COMPLETED, Status.FAILED}; + Map queues = new HashMap<>(); + for (Status status : statuses) { + String queuePrefix = + StringUtils.isBlank(properties.getListenerQueuePrefix()) + ? conductorProperties.getAppId() + "_amqp_notify_" + stack + : properties.getListenerQueuePrefix(); + + String queueName = queuePrefix + status.name(); + + final ObservableQueue queue = new Builder(properties).build(useExchange, queueName); + queues.put(status, queue); + } + + return queues; + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java new file mode 100644 index 000000000..ac0a08dae --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java @@ -0,0 +1,313 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.config; + +import java.time.Duration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import com.netflix.conductor.contribs.queue.amqp.util.RetryType; + +import com.rabbitmq.client.AMQP.PROTOCOL; +import com.rabbitmq.client.ConnectionFactory; + +@ConfigurationProperties("conductor.event-queues.amqp") +public class AMQPEventQueueProperties { + + private int batchSize = 1; + + private Duration pollTimeDuration = Duration.ofMillis(100); + + private String hosts = ConnectionFactory.DEFAULT_HOST; + + private String username = ConnectionFactory.DEFAULT_USER; + + private String password = ConnectionFactory.DEFAULT_PASS; + + private String virtualHost = ConnectionFactory.DEFAULT_VHOST; + + private int port = PROTOCOL.PORT; + + private int connectionTimeoutInMilliSecs = 180000; + private int networkRecoveryIntervalInMilliSecs = 5000; + private int requestHeartbeatTimeoutInSecs = 30; + private int handshakeTimeoutInMilliSecs = 180000; + private int maxChannelCount = 5000; + private int limit = 50; + private int duration = 1000; + private RetryType retryType = RetryType.REGULARINTERVALS; + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public RetryType getType() { + return retryType; + } + + public void setType(RetryType type) { + this.retryType = type; + } + + public int getConnectionTimeoutInMilliSecs() { + return connectionTimeoutInMilliSecs; + } + + public void setConnectionTimeoutInMilliSecs(int connectionTimeoutInMilliSecs) { + this.connectionTimeoutInMilliSecs = connectionTimeoutInMilliSecs; + } + + public int getHandshakeTimeoutInMilliSecs() { + return handshakeTimeoutInMilliSecs; + } + + public void setHandshakeTimeoutInMilliSecs(int handshakeTimeoutInMilliSecs) { + this.handshakeTimeoutInMilliSecs = handshakeTimeoutInMilliSecs; + } + + public int getMaxChannelCount() { + return maxChannelCount; + } + + public void setMaxChannelCount(int maxChannelCount) { + this.maxChannelCount = maxChannelCount; + } + + private boolean useNio = false; + + private boolean durable = true; + + private boolean exclusive = false; + + private boolean autoDelete = false; + + private String contentType = "application/json"; + + private String contentEncoding = "UTF-8"; + + private String exchangeType = "topic"; + + private String queueType = "classic"; + + private boolean sequentialMsgProcessing = true; + + private int deliveryMode = 2; + + private boolean useExchange = true; + + private String listenerQueuePrefix = ""; + + private boolean useSslProtocol = false; + + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public Duration getPollTimeDuration() { + return pollTimeDuration; + } + + public void setPollTimeDuration(Duration pollTimeDuration) { + this.pollTimeDuration = pollTimeDuration; + } + + public String getHosts() { + return hosts; + } + + public void setHosts(String hosts) { + this.hosts = hosts; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getVirtualHost() { + return virtualHost; + } + + public void setVirtualHost(String virtualHost) { + this.virtualHost = virtualHost; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isUseNio() { + return useNio; + } + + public void setUseNio(boolean useNio) { + this.useNio = useNio; + } + + public boolean isDurable() { + return durable; + } + + public void setDurable(boolean durable) { + this.durable = durable; + } + + public boolean isExclusive() { + return exclusive; + } + + public void setExclusive(boolean exclusive) { + this.exclusive = exclusive; + } + + public boolean isAutoDelete() { + return autoDelete; + } + + public void setAutoDelete(boolean autoDelete) { + this.autoDelete = autoDelete; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getContentEncoding() { + return contentEncoding; + } + + public void setContentEncoding(String contentEncoding) { + this.contentEncoding = contentEncoding; + } + + public String getExchangeType() { + return exchangeType; + } + + public void setExchangeType(String exchangeType) { + this.exchangeType = exchangeType; + } + + public int getDeliveryMode() { + return deliveryMode; + } + + public void setDeliveryMode(int deliveryMode) { + this.deliveryMode = deliveryMode; + } + + public boolean isUseExchange() { + return useExchange; + } + + public void setUseExchange(boolean useExchange) { + this.useExchange = useExchange; + } + + public String getListenerQueuePrefix() { + return listenerQueuePrefix; + } + + public void setListenerQueuePrefix(String listenerQueuePrefix) { + this.listenerQueuePrefix = listenerQueuePrefix; + } + + public String getQueueType() { + return queueType; + } + + public boolean isUseSslProtocol() { + return useSslProtocol; + } + + public void setUseSslProtocol(boolean useSslProtocol) { + this.useSslProtocol = useSslProtocol; + } + + /** + * @param queueType Supports two queue types, 'classic' and 'quorum'. Classic will be be + * deprecated in 2022 and its usage discouraged from RabbitMQ community. So not using enum + * type here to hold different values. + */ + public void setQueueType(String queueType) { + this.queueType = queueType; + } + + /** + * @return the sequentialMsgProcessing + */ + public boolean isSequentialMsgProcessing() { + return sequentialMsgProcessing; + } + + /** + * @param sequentialMsgProcessing the sequentialMsgProcessing to set Supports sequential and + * parallel message processing capabilities. In parallel message processing, number of + * threads are controlled by batch size. No thread control or execution framework required + * here as threads are limited and short-lived. + */ + public void setSequentialMsgProcessing(boolean sequentialMsgProcessing) { + this.sequentialMsgProcessing = sequentialMsgProcessing; + } + + public int getNetworkRecoveryIntervalInMilliSecs() { + return networkRecoveryIntervalInMilliSecs; + } + + public void setNetworkRecoveryIntervalInMilliSecs(int networkRecoveryIntervalInMilliSecs) { + this.networkRecoveryIntervalInMilliSecs = networkRecoveryIntervalInMilliSecs; + } + + public int getRequestHeartbeatTimeoutInSecs() { + return requestHeartbeatTimeoutInSecs; + } + + public void setRequestHeartbeatTimeoutInSecs(int requestHeartbeatTimeoutInSecs) { + this.requestHeartbeatTimeoutInSecs = requestHeartbeatTimeoutInSecs; + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java new file mode 100644 index 000000000..51bcc4082 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.NonNull; + +import com.netflix.conductor.contribs.queue.amqp.AMQPObservableQueue; +import com.netflix.conductor.contribs.queue.amqp.AMQPObservableQueue.Builder; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +/** + * @author Ritu Parathody + */ +public class AMQPEventQueueProvider implements EventQueueProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(AMQPEventQueueProvider.class); + protected Map queues = new ConcurrentHashMap<>(); + private final boolean useExchange; + private final AMQPEventQueueProperties properties; + private final String queueType; + + public AMQPEventQueueProvider( + AMQPEventQueueProperties properties, String queueType, boolean useExchange) { + this.properties = properties; + this.queueType = queueType; + this.useExchange = useExchange; + } + + @Override + public String getQueueType() { + return queueType; + } + + @Override + @NonNull + public ObservableQueue getQueue(String queueURI) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Retrieve queue with URI {}", queueURI); + } + // Build the queue with the inner Builder class of AMQPObservableQueue + return queues.computeIfAbsent(queueURI, q -> new Builder(properties).build(useExchange, q)); + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java new file mode 100644 index 000000000..736cb5f42 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.config; + +import com.netflix.conductor.contribs.queue.amqp.util.RetryType; + +public class AMQPRetryPattern { + + private int limit = 50; + private int duration = 1000; + private RetryType type = RetryType.REGULARINTERVALS; + + public AMQPRetryPattern() {} + + public AMQPRetryPattern(int limit, int duration, RetryType type) { + this.limit = limit; + this.duration = duration; + this.type = type; + } + + /** + * This gets executed if the retry index is within the allowed limits, otherwise exception will + * be thrown. + * + * @throws Exception + */ + public void continueOrPropogate(Exception ex, int retryIndex) throws Exception { + if (retryIndex > limit) { + throw ex; + } + // Regular Intervals is the default + long waitDuration = duration; + if (type == RetryType.INCREMENTALINTERVALS) { + waitDuration = duration * retryIndex; + } else if (type == RetryType.EXPONENTIALBACKOFF) { + waitDuration = (long) Math.pow(2, retryIndex) * duration; + } + try { + Thread.sleep(waitDuration); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java new file mode 100644 index 000000000..36bd7a6cf --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.util; + +/** + * @author Ritu Parathody + */ +public enum AMQPConfigurations { + + // queue exchange settings + PARAM_EXCHANGE_TYPE("exchangeType"), + PARAM_QUEUE_NAME("bindQueueName"), + PARAM_ROUTING_KEY("routingKey"), + PARAM_DELIVERY_MODE("deliveryMode"), + PARAM_DURABLE("durable"), + PARAM_EXCLUSIVE("exclusive"), + PARAM_AUTO_DELETE("autoDelete"), + PARAM_MAX_PRIORITY("maxPriority"); + + String propertyName; + + AMQPConfigurations(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public String toString() { + return propertyName; + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java new file mode 100644 index 000000000..fc9aa9892 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java @@ -0,0 +1,91 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.util; + +/** + * @author Ritu Parathody + */ +public class AMQPConstants { + + /** this when set will create a rabbitmq queue */ + public static String AMQP_QUEUE_TYPE = "amqp_queue"; + + /** this when set will create a rabbitmq exchange */ + public static String AMQP_EXCHANGE_TYPE = "amqp_exchange"; + + public static String PROPERTY_KEY_TEMPLATE = "conductor.event-queues.amqp.%s"; + + /** default content type for the message read from rabbitmq */ + public static String DEFAULT_CONTENT_TYPE = "application/json"; + + /** default encoding for the message read from rabbitmq */ + public static String DEFAULT_CONTENT_ENCODING = "UTF-8"; + + /** default rabbitmq exchange type */ + public static String DEFAULT_EXCHANGE_TYPE = "topic"; + + /** + * default rabbitmq durability When set to true the queues are persisted to the disk. + * + *

{@see RabbitMQ}. + */ + public static boolean DEFAULT_DURABLE = true; + + /** + * default rabbitmq exclusivity When set to true the queues can be only used by one connection. + * + *

{@see RabbitMQ}. + */ + public static boolean DEFAULT_EXCLUSIVE = false; + + /** + * default rabbitmq auto delete When set to true the queues will be deleted when the last + * consumer is cancelled + * + *

{@see RabbitMQ}. + */ + public static boolean DEFAULT_AUTO_DELETE = false; + + /** + * default rabbitmq delivery mode This is a property of the message When set to 1 the will be + * non persistent and 2 will be persistent {@see Consumer Prefetch}. + */ + public static int DEFAULT_BATCH_SIZE = 1; + + /** + * default rabbitmq delivery mode This is a property of the amqp implementation which sets teh + * polling time to drain the in-memory queue. + */ + public static int DEFAULT_POLL_TIME_MS = 100; + + // info channel messages. + public static final String INFO_CHANNEL_BORROW_SUCCESS = + "Borrowed the channel object from the channel pool for " + "the connection type [%s]"; + public static final String INFO_CHANNEL_RETURN_SUCCESS = + "Returned the borrowed channel object to the pool for " + "the connection type [%s]"; + public static final String INFO_CHANNEL_CREATION_SUCCESS = + "Channels are not available in the pool. Created a" + + " channel for the connection type [%s]"; + public static final String INFO_CHANNEL_RESET_SUCCESS = + "No proper channels available in the pool. Created a " + + "channel for the connection type [%s]"; +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java new file mode 100644 index 000000000..4684d16a3 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java @@ -0,0 +1,315 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.util; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProperties; + +import static com.netflix.conductor.contribs.queue.amqp.util.AMQPConfigurations.*; + +/** + * @author Ritu Parathody + */ +public class AMQPSettings { + + private static final Pattern URI_PATTERN = + Pattern.compile( + "^(?:amqp\\_(queue|exchange))?\\:?(?[^\\?]+)\\??(?.*)$", + Pattern.CASE_INSENSITIVE); + + private String queueOrExchangeName; + private String eventName; + private String exchangeType; + private String exchangeBoundQueueName; + private String queueType; + private String routingKey; + private final String contentEncoding; + private final String contentType; + private boolean durable; + private boolean exclusive; + private boolean autoDelete; + private boolean sequentialProcessing; + private int deliveryMode; + + private final Map arguments = new HashMap<>(); + private static final Logger LOGGER = LoggerFactory.getLogger(AMQPSettings.class); + + public AMQPSettings(final AMQPEventQueueProperties properties) { + // Initialize with a default values + durable = properties.isDurable(); + exclusive = properties.isExclusive(); + autoDelete = properties.isAutoDelete(); + contentType = properties.getContentType(); + contentEncoding = properties.getContentEncoding(); + exchangeType = properties.getExchangeType(); + routingKey = StringUtils.EMPTY; + queueType = properties.getQueueType(); + sequentialProcessing = properties.isSequentialMsgProcessing(); + // Set common settings for publishing and consuming + setDeliveryMode(properties.getDeliveryMode()); + } + + public final boolean isDurable() { + return durable; + } + + public final boolean isExclusive() { + return exclusive; + } + + public final boolean autoDelete() { + return autoDelete; + } + + public final Map getArguments() { + return arguments; + } + + public final String getContentEncoding() { + return contentEncoding; + } + + /** + * Use queue for publishing + * + * @param queueName the name of queue + */ + public void setQueue(String queueName) { + if (StringUtils.isEmpty(queueName)) { + throw new IllegalArgumentException("Queue name for publishing is undefined"); + } + this.queueOrExchangeName = queueName; + } + + public String getQueueOrExchangeName() { + return queueOrExchangeName; + } + + public String getExchangeBoundQueueName() { + if (StringUtils.isEmpty(exchangeBoundQueueName)) { + return String.format("bound_to_%s", queueOrExchangeName); + } + return exchangeBoundQueueName; + } + + public String getExchangeType() { + return exchangeType; + } + + public String getRoutingKey() { + return routingKey; + } + + public int getDeliveryMode() { + return deliveryMode; + } + + public AMQPSettings setDeliveryMode(int deliveryMode) { + if (deliveryMode != 1 && deliveryMode != 2) { + throw new IllegalArgumentException("Delivery mode must be 1 or 2"); + } + this.deliveryMode = deliveryMode; + return this; + } + + public String getContentType() { + return contentType; + } + + /** + * Complete settings from the queue URI. + * + *

Example for queue: + * + *

+     * amqp_queue:myQueue?deliveryMode=1&autoDelete=true&exclusive=true
+     * 
+ * + * Example for exchange: + * + *
+     * amqp_exchange:myExchange?bindQueueName=myQueue&exchangeType=topic&routingKey=myRoutingKey&exclusive=true
+     * 
+ * + * @param queueURI + * @return + */ + public final AMQPSettings fromURI(final String queueURI) { + final Matcher matcher = URI_PATTERN.matcher(queueURI); + if (!matcher.matches()) { + throw new IllegalArgumentException("Queue URI doesn't matches the expected regexp"); + } + + // Set name of queue or exchange from group "name" + LOGGER.info("Queue URI:{}", queueURI); + queueOrExchangeName = matcher.group("name"); + eventName = queueURI; + if (matcher.groupCount() > 1) { + final String queryParams = matcher.group("params"); + if (StringUtils.isNotEmpty(queryParams)) { + // Handle parameters + Arrays.stream(queryParams.split("\\s*\\&\\s*")) + .forEach( + param -> { + final String[] kv = param.split("\\s*=\\s*"); + if (kv.length == 2) { + if (kv[0].equalsIgnoreCase( + String.valueOf(PARAM_EXCHANGE_TYPE))) { + String value = kv[1]; + if (StringUtils.isEmpty(value)) { + throw new IllegalArgumentException( + "The provided exchange type is empty"); + } + exchangeType = value; + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_QUEUE_NAME)))) { + exchangeBoundQueueName = kv[1]; + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_ROUTING_KEY)))) { + String value = kv[1]; + if (StringUtils.isEmpty(value)) { + throw new IllegalArgumentException( + "The provided routing key is empty"); + } + routingKey = value; + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_DURABLE)))) { + durable = Boolean.parseBoolean(kv[1]); + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_EXCLUSIVE)))) { + exclusive = Boolean.parseBoolean(kv[1]); + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_AUTO_DELETE)))) { + autoDelete = Boolean.parseBoolean(kv[1]); + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_DELIVERY_MODE)))) { + setDeliveryMode(Integer.parseInt(kv[1])); + } + if (kv[0].equalsIgnoreCase( + (String.valueOf(PARAM_MAX_PRIORITY)))) { + arguments.put("x-max-priority", Integer.valueOf(kv[1])); + } + } + }); + } + } + return this; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof AMQPSettings)) return false; + AMQPSettings other = (AMQPSettings) obj; + return Objects.equals(arguments, other.arguments) + && autoDelete == other.autoDelete + && Objects.equals(contentEncoding, other.contentEncoding) + && Objects.equals(contentType, other.contentType) + && deliveryMode == other.deliveryMode + && durable == other.durable + && Objects.equals(eventName, other.eventName) + && Objects.equals(exchangeType, other.exchangeType) + && exclusive == other.exclusive + && Objects.equals(queueOrExchangeName, other.queueOrExchangeName) + && Objects.equals(exchangeBoundQueueName, other.exchangeBoundQueueName) + && Objects.equals(queueType, other.queueType) + && Objects.equals(routingKey, other.routingKey) + && sequentialProcessing == other.sequentialProcessing; + } + + @Override + public int hashCode() { + return Objects.hash( + arguments, + autoDelete, + contentEncoding, + contentType, + deliveryMode, + durable, + eventName, + exchangeType, + exclusive, + queueOrExchangeName, + exchangeBoundQueueName, + queueType, + routingKey, + sequentialProcessing); + } + + @Override + public String toString() { + return "AMQPSettings [queueOrExchangeName=" + + queueOrExchangeName + + ", eventName=" + + eventName + + ", exchangeType=" + + exchangeType + + ", exchangeQueueName=" + + exchangeBoundQueueName + + ", queueType=" + + queueType + + ", routingKey=" + + routingKey + + ", contentEncoding=" + + contentEncoding + + ", contentType=" + + contentType + + ", durable=" + + durable + + ", exclusive=" + + exclusive + + ", autoDelete=" + + autoDelete + + ", sequentialProcessing=" + + sequentialProcessing + + ", deliveryMode=" + + deliveryMode + + ", arguments=" + + arguments + + "]"; + } + + public String getEventName() { + return eventName; + } + + /** + * @return the queueType + */ + public String getQueueType() { + return queueType; + } + + /** + * @return the sequentialProcessing + */ + public boolean isSequentialProcessing() { + return sequentialProcessing; + } +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java new file mode 100644 index 000000000..c6cfdc273 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.util; + +public enum ConnectionType { + PUBLISHER, + SUBSCRIBER +} diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java new file mode 100644 index 000000000..00c683527 --- /dev/null +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java @@ -0,0 +1,20 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp.util; + +/** RetryType holds the retry type */ +public enum RetryType { + REGULARINTERVALS, + EXPONENTIALBACKOFF, + INCREMENTALINTERVALS +} diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java new file mode 100644 index 000000000..b881e4cc1 --- /dev/null +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp; + +import java.time.Duration; + +import org.junit.Before; +import org.junit.Test; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProperties; +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProvider; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPConstants; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import com.rabbitmq.client.AMQP.PROTOCOL; +import com.rabbitmq.client.ConnectionFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AMQPEventQueueProviderTest { + + private AMQPEventQueueProperties properties; + + @Before + public void setUp() { + properties = mock(AMQPEventQueueProperties.class); + when(properties.getBatchSize()).thenReturn(1); + when(properties.getPollTimeDuration()).thenReturn(Duration.ofMillis(100)); + when(properties.getHosts()).thenReturn(ConnectionFactory.DEFAULT_HOST); + when(properties.getUsername()).thenReturn(ConnectionFactory.DEFAULT_USER); + when(properties.getPassword()).thenReturn(ConnectionFactory.DEFAULT_PASS); + when(properties.getVirtualHost()).thenReturn(ConnectionFactory.DEFAULT_VHOST); + when(properties.getPort()).thenReturn(PROTOCOL.PORT); + when(properties.getConnectionTimeoutInMilliSecs()).thenReturn(60000); + when(properties.isUseNio()).thenReturn(false); + when(properties.isDurable()).thenReturn(true); + when(properties.isExclusive()).thenReturn(false); + when(properties.isAutoDelete()).thenReturn(false); + when(properties.getContentType()).thenReturn("application/json"); + when(properties.getContentEncoding()).thenReturn("UTF-8"); + when(properties.getExchangeType()).thenReturn("topic"); + when(properties.getDeliveryMode()).thenReturn(2); + when(properties.isUseExchange()).thenReturn(true); + } + + @Test + public void testAMQPEventQueueProvider_defaultconfig_exchange() { + String exchangestring = + "amqp_exchange:myExchangeName?exchangeType=topic&routingKey=test&deliveryMode=2"; + AMQPEventQueueProvider eventqProvider = + new AMQPEventQueueProvider(properties, "amqp_exchange", true); + ObservableQueue queue = eventqProvider.getQueue(exchangestring); + assertNotNull(queue); + assertEquals(exchangestring, queue.getName()); + assertEquals(AMQPConstants.AMQP_EXCHANGE_TYPE, queue.getType()); + } + + @Test + public void testAMQPEventQueueProvider_defaultconfig_queue() { + String exchangestring = + "amqp_queue:myQueueName?deliveryMode=2&durable=false&autoDelete=true&exclusive=true"; + AMQPEventQueueProvider eventqProvider = + new AMQPEventQueueProvider(properties, "amqp_queue", false); + ObservableQueue queue = eventqProvider.getQueue(exchangestring); + assertNotNull(queue); + assertEquals(exchangestring, queue.getName()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE, queue.getType()); + } +} diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java new file mode 100644 index 000000000..9a16cae64 --- /dev/null +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java @@ -0,0 +1,911 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.internal.stubbing.answers.DoesNothing; +import org.mockito.stubbing.OngoingStubbing; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProperties; +import com.netflix.conductor.contribs.queue.amqp.config.AMQPRetryPattern; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPConstants; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPSettings; +import com.netflix.conductor.contribs.queue.amqp.util.RetryType; +import com.netflix.conductor.core.events.queue.Message; + +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.AMQP.PROTOCOL; +import com.rabbitmq.client.AMQP.Queue.DeclareOk; +import com.rabbitmq.client.Address; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.Consumer; +import com.rabbitmq.client.Envelope; +import com.rabbitmq.client.GetResponse; +import com.rabbitmq.client.impl.AMQImpl; +import rx.Observable; +import rx.observers.Subscribers; +import rx.observers.TestSubscriber; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class AMQPObservableQueueTest { + + final int batchSize = 10; + final int pollTimeMs = 500; + + Address[] addresses; + AMQPEventQueueProperties properties; + + @Before + public void setUp() { + properties = mock(AMQPEventQueueProperties.class); + when(properties.getBatchSize()).thenReturn(1); + when(properties.getPollTimeDuration()).thenReturn(Duration.ofMillis(100)); + when(properties.getHosts()).thenReturn(ConnectionFactory.DEFAULT_HOST); + when(properties.getUsername()).thenReturn(ConnectionFactory.DEFAULT_USER); + when(properties.getPassword()).thenReturn(ConnectionFactory.DEFAULT_PASS); + when(properties.getVirtualHost()).thenReturn(ConnectionFactory.DEFAULT_VHOST); + when(properties.getPort()).thenReturn(PROTOCOL.PORT); + when(properties.getConnectionTimeoutInMilliSecs()).thenReturn(60000); + when(properties.isUseNio()).thenReturn(false); + when(properties.isDurable()).thenReturn(true); + when(properties.isExclusive()).thenReturn(false); + when(properties.isAutoDelete()).thenReturn(false); + when(properties.getContentType()).thenReturn("application/json"); + when(properties.getContentEncoding()).thenReturn("UTF-8"); + when(properties.getExchangeType()).thenReturn("topic"); + when(properties.getDeliveryMode()).thenReturn(2); + when(properties.isUseExchange()).thenReturn(true); + addresses = new Address[] {new Address("localhost", PROTOCOL.PORT)}; + AMQPConnection.setAMQPConnection(null); + } + + List buildQueue(final Random random, final int bound) { + final LinkedList queue = new LinkedList(); + for (int i = 0; i < bound; i++) { + AMQP.BasicProperties props = mock(AMQP.BasicProperties.class); + when(props.getMessageId()).thenReturn(UUID.randomUUID().toString()); + Envelope envelope = mock(Envelope.class); + when(envelope.getDeliveryTag()).thenReturn(random.nextLong()); + GetResponse response = mock(GetResponse.class); + when(response.getProps()).thenReturn(props); + when(response.getEnvelope()).thenReturn(envelope); + when(response.getBody()).thenReturn("{}".getBytes()); + when(response.getMessageCount()).thenReturn(bound - i); + queue.add(response); + } + return queue; + } + + Channel mockBaseChannel() throws IOException, TimeoutException { + Channel channel = mock(Channel.class); + when(channel.isOpen()).thenReturn(Boolean.TRUE); + /* + * doAnswer(invocation -> { when(channel.isOpen()).thenReturn(Boolean.FALSE); + * return DoesNothing.doesNothing(); }).when(channel).close(); + */ + return channel; + } + + Channel mockChannelForQueue( + Channel channel, + boolean isWorking, + boolean exists, + String name, + List queue) + throws IOException { + // queueDeclarePassive + final AMQImpl.Queue.DeclareOk queueDeclareOK = + new AMQImpl.Queue.DeclareOk(name, queue.size(), 1); + if (exists) { + when(channel.queueDeclarePassive(eq(name))).thenReturn(queueDeclareOK); + } else { + when(channel.queueDeclarePassive(eq(name))) + .thenThrow(new IOException("Queue " + name + " exists")); + } + // queueDeclare + OngoingStubbing declareOkOngoingStubbing = + when(channel.queueDeclare( + eq(name), anyBoolean(), anyBoolean(), anyBoolean(), anyMap())) + .thenReturn(queueDeclareOK); + if (!isWorking) { + declareOkOngoingStubbing.thenThrow( + new IOException("Cannot declare queue " + name), + new RuntimeException("Not working")); + } + // messageCount + when(channel.messageCount(eq(name))).thenReturn((long) queue.size()); + // basicGet + OngoingStubbing getResponseOngoingStubbing = + Mockito.when(channel.basicConsume(eq(name), anyBoolean(), any(Consumer.class))) + .thenReturn(name); + if (!isWorking) { + getResponseOngoingStubbing.thenThrow( + new IOException("Not working"), new RuntimeException("Not working")); + } + // basicPublish + if (isWorking) { + doNothing() + .when(channel) + .basicPublish( + eq(StringUtils.EMPTY), + eq(name), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } else { + doThrow(new IOException("Not working")) + .when(channel) + .basicPublish( + eq(StringUtils.EMPTY), + eq(name), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } + return channel; + } + + Channel mockChannelForExchange( + Channel channel, + boolean isWorking, + boolean exists, + String queueName, + String name, + String type, + String routingKey, + List queue) + throws IOException { + // exchangeDeclarePassive + final AMQImpl.Exchange.DeclareOk exchangeDeclareOK = new AMQImpl.Exchange.DeclareOk(); + if (exists) { + when(channel.exchangeDeclarePassive(eq(name))).thenReturn(exchangeDeclareOK); + } else { + when(channel.exchangeDeclarePassive(eq(name))) + .thenThrow(new IOException("Exchange " + name + " exists")); + } + // exchangeDeclare + OngoingStubbing declareOkOngoingStubbing = + when(channel.exchangeDeclare( + eq(name), eq(type), anyBoolean(), anyBoolean(), anyMap())) + .thenReturn(exchangeDeclareOK); + if (!isWorking) { + declareOkOngoingStubbing.thenThrow( + new IOException("Cannot declare exchange " + name + " of type " + type), + new RuntimeException("Not working")); + } + // queueDeclarePassive + final AMQImpl.Queue.DeclareOk queueDeclareOK = + new AMQImpl.Queue.DeclareOk(queueName, queue.size(), 1); + if (exists) { + when(channel.queueDeclarePassive(eq(queueName))).thenReturn(queueDeclareOK); + } else { + when(channel.queueDeclarePassive(eq(queueName))) + .thenThrow(new IOException("Queue " + queueName + " exists")); + } + // queueDeclare + when(channel.queueDeclare( + eq(queueName), anyBoolean(), anyBoolean(), anyBoolean(), anyMap())) + .thenReturn(queueDeclareOK); + // queueBind + when(channel.queueBind(eq(queueName), eq(name), eq(routingKey))) + .thenReturn(new AMQImpl.Queue.BindOk()); + // messageCount + when(channel.messageCount(eq(name))).thenReturn((long) queue.size()); + // basicGet + + OngoingStubbing getResponseOngoingStubbing = + Mockito.when(channel.basicConsume(eq(queueName), anyBoolean(), any(Consumer.class))) + .thenReturn(queueName); + + if (!isWorking) { + getResponseOngoingStubbing.thenThrow( + new IOException("Not working"), new RuntimeException("Not working")); + } + // basicPublish + if (isWorking) { + doNothing() + .when(channel) + .basicPublish( + eq(name), + eq(routingKey), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } else { + doThrow(new IOException("Not working")) + .when(channel) + .basicPublish( + eq(name), + eq(routingKey), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } + return channel; + } + + Connection mockGoodConnection(Channel channel) throws IOException { + Connection connection = mock(Connection.class); + when(connection.createChannel()).thenReturn(channel); + when(connection.isOpen()).thenReturn(Boolean.TRUE); + /* + * doAnswer(invocation -> { when(connection.isOpen()).thenReturn(Boolean.FALSE); + * return DoesNothing.doesNothing(); }).when(connection).close(); + */ return connection; + } + + Connection mockBadConnection() throws IOException { + Connection connection = mock(Connection.class); + when(connection.createChannel()).thenThrow(new IOException("Can't create channel")); + when(connection.isOpen()).thenReturn(Boolean.TRUE); + doThrow(new IOException("Can't close connection")).when(connection).close(); + return connection; + } + + ConnectionFactory mockConnectionFactory(Connection connection) + throws IOException, TimeoutException { + ConnectionFactory connectionFactory = mock(ConnectionFactory.class); + when(connectionFactory.newConnection(eq(addresses), Mockito.anyString())) + .thenReturn(connection); + return connectionFactory; + } + + void runObserve( + Channel channel, + AMQPObservableQueue observableQueue, + String queueName, + boolean useWorkingChannel, + int batchSize) + throws IOException { + + final List found = new ArrayList<>(batchSize); + TestSubscriber subscriber = TestSubscriber.create(Subscribers.create(found::add)); + rx.Observable observable = + observableQueue.observe().take(pollTimeMs * 2, TimeUnit.MILLISECONDS); + assertNotNull(observable); + observable.subscribe(subscriber); + subscriber.awaitTerminalEvent(); + subscriber.assertNoErrors(); + subscriber.assertCompleted(); + if (useWorkingChannel) { + verify(channel, atLeast(1)) + .basicConsume(eq(queueName), anyBoolean(), any(Consumer.class)); + doNothing().when(channel).basicAck(anyLong(), eq(false)); + doAnswer(DoesNothing.doesNothing()).when(channel).basicAck(anyLong(), eq(false)); + observableQueue.ack(Collections.synchronizedList(found)); + } else { + assertNotNull(found); + assertTrue(found.isEmpty()); + } + observableQueue.close(); + } + + @Test + public void + testGetMessagesFromExistingExchangeWithDurableExclusiveAutoDeleteQueueConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromExchangeAndCustomConfigurationFromURI( + channel, connection, true, true, true, true, true); + } + + @Test + public void testGetMessagesFromExistingExchangeWithDefaultConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromExchangeAndDefaultConfiguration(channel, connection, true, true); + } + + @Test + public void testPublishMessagesToNotExistingExchangeAndDefaultConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testPublishMessagesToExchangeAndDefaultConfiguration(channel, connection, false, true); + } + + @Test + public void testAck() throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + final Random random = new Random(); + + final String name = RandomStringUtils.randomAlphabetic(30), + type = "topic", + routingKey = RandomStringUtils.randomAlphabetic(30); + AMQPRetryPattern retrySettings = null; + final AMQPSettings settings = + new AMQPSettings(properties) + .fromURI( + "amqp_exchange:" + + name + + "?exchangeType=" + + type + + "&routingKey=" + + routingKey); + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + true, + settings, + retrySettings, + batchSize, + pollTimeMs); + List messages = new LinkedList<>(); + Message msg = new Message(); + msg.setId("0e3eef8f-ebb1-4244-9665-759ab5bdf433"); + msg.setPayload("Payload"); + msg.setReceipt("1"); + messages.add(msg); + List failedMessages = observableQueue.ack(messages); + assertNotNull(failedMessages); + assertTrue(failedMessages.isEmpty()); + } + + private void testGetMessagesFromExchangeAndDefaultConfiguration( + Channel channel, Connection connection, boolean exists, boolean useWorkingChannel) + throws IOException, TimeoutException { + + final Random random = new Random(); + + final String name = RandomStringUtils.randomAlphabetic(30), + type = "topic", + routingKey = RandomStringUtils.randomAlphabetic(30); + final String queueName = String.format("bound_to_%s", name); + + final AMQPSettings settings = + new AMQPSettings(properties) + .fromURI( + "amqp_exchange:" + + name + + "?exchangeType=" + + type + + "&routingKey=" + + routingKey); + assertTrue(settings.isDurable()); + assertFalse(settings.isExclusive()); + assertFalse(settings.autoDelete()); + assertEquals(2, settings.getDeliveryMode()); + assertEquals(name, settings.getQueueOrExchangeName()); + assertEquals(type, settings.getExchangeType()); + assertEquals(routingKey, settings.getRoutingKey()); + assertEquals(queueName, settings.getExchangeBoundQueueName()); + + List queue = buildQueue(random, batchSize); + channel = + mockChannelForExchange( + channel, + useWorkingChannel, + exists, + queueName, + name, + type, + routingKey, + queue); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + true, + settings, + retrySettings, + batchSize, + pollTimeMs); + + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_EXCHANGE_TYPE, observableQueue.getType()); + assertEquals( + AMQPConstants.AMQP_EXCHANGE_TYPE + + ":" + + name + + "?exchangeType=" + + type + + "&routingKey=" + + routingKey, + observableQueue.getName()); + assertEquals(name, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + + runObserve(channel, observableQueue, queueName, useWorkingChannel, batchSize); + + if (useWorkingChannel) { + verify(channel, atLeastOnce()) + .exchangeDeclare( + eq(name), + eq(type), + eq(settings.isDurable()), + eq(settings.autoDelete()), + eq(Collections.emptyMap())); + verify(channel, atLeastOnce()) + .queueDeclare( + eq(queueName), + eq(settings.isDurable()), + eq(settings.isExclusive()), + eq(settings.autoDelete()), + anyMap()); + + verify(channel, atLeastOnce()).queueBind(eq(queueName), eq(name), eq(routingKey)); + } + } + + private void testGetMessagesFromExchangeAndCustomConfigurationFromURI( + Channel channel, + Connection connection, + boolean exists, + boolean useWorkingChannel, + boolean durable, + boolean exclusive, + boolean autoDelete) + throws IOException, TimeoutException { + + final Random random = new Random(); + + final String name = RandomStringUtils.randomAlphabetic(30), + type = "topic", + routingKey = RandomStringUtils.randomAlphabetic(30); + final String queueName = String.format("bound_to_%s", name); + + final AMQPSettings settings = + new AMQPSettings(properties) + .fromURI( + "amqp_exchange:" + + name + + "?exchangeType=" + + type + + "&bindQueueName=" + + queueName + + "&routingKey=" + + routingKey + + "&deliveryMode=2" + + "&durable=" + + durable + + "&exclusive=" + + exclusive + + "&autoDelete=" + + autoDelete); + assertEquals(durable, settings.isDurable()); + assertEquals(exclusive, settings.isExclusive()); + assertEquals(autoDelete, settings.autoDelete()); + assertEquals(2, settings.getDeliveryMode()); + assertEquals(name, settings.getQueueOrExchangeName()); + assertEquals(type, settings.getExchangeType()); + assertEquals(queueName, settings.getExchangeBoundQueueName()); + assertEquals(routingKey, settings.getRoutingKey()); + + List queue = buildQueue(random, batchSize); + channel = + mockChannelForExchange( + channel, + useWorkingChannel, + exists, + queueName, + name, + type, + routingKey, + queue); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + true, + settings, + retrySettings, + batchSize, + pollTimeMs); + + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_EXCHANGE_TYPE, observableQueue.getType()); + assertEquals( + AMQPConstants.AMQP_EXCHANGE_TYPE + + ":" + + name + + "?exchangeType=" + + type + + "&bindQueueName=" + + queueName + + "&routingKey=" + + routingKey + + "&deliveryMode=2" + + "&durable=" + + durable + + "&exclusive=" + + exclusive + + "&autoDelete=" + + autoDelete, + observableQueue.getName()); + assertEquals(name, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + + runObserve(channel, observableQueue, queueName, useWorkingChannel, batchSize); + + if (useWorkingChannel) { + verify(channel, atLeastOnce()) + .exchangeDeclare( + eq(name), + eq(type), + eq(settings.isDurable()), + eq(settings.autoDelete()), + eq(Collections.emptyMap())); + verify(channel, atLeastOnce()) + .queueDeclare( + eq(queueName), + eq(settings.isDurable()), + eq(settings.isExclusive()), + eq(settings.autoDelete()), + anyMap()); + + verify(channel, atLeastOnce()).queueBind(eq(queueName), eq(name), eq(routingKey)); + } + } + + private void testPublishMessagesToExchangeAndDefaultConfiguration( + Channel channel, Connection connection, boolean exists, boolean useWorkingChannel) + throws IOException, TimeoutException { + final Random random = new Random(); + + final String name = RandomStringUtils.randomAlphabetic(30), + type = "topic", + queueName = RandomStringUtils.randomAlphabetic(30), + routingKey = RandomStringUtils.randomAlphabetic(30); + + final AMQPSettings settings = + new AMQPSettings(properties) + .fromURI( + "amqp_exchange:" + + name + + "?exchangeType=" + + type + + "&routingKey=" + + routingKey + + "&deliveryMode=2&durable=true&exclusive=false&autoDelete=true"); + assertTrue(settings.isDurable()); + assertFalse(settings.isExclusive()); + assertTrue(settings.autoDelete()); + assertEquals(2, settings.getDeliveryMode()); + assertEquals(name, settings.getQueueOrExchangeName()); + assertEquals(type, settings.getExchangeType()); + assertEquals(routingKey, settings.getRoutingKey()); + + List queue = buildQueue(random, batchSize); + channel = + mockChannelForExchange( + channel, + useWorkingChannel, + exists, + queueName, + name, + type, + routingKey, + queue); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + true, + settings, + retrySettings, + batchSize, + pollTimeMs); + + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_EXCHANGE_TYPE, observableQueue.getType()); + assertEquals( + AMQPConstants.AMQP_EXCHANGE_TYPE + + ":" + + name + + "?exchangeType=" + + type + + "&routingKey=" + + routingKey + + "&deliveryMode=2&durable=true&exclusive=false&autoDelete=true", + observableQueue.getName()); + assertEquals(name, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + + List messages = new LinkedList<>(); + Observable.range(0, batchSize) + .forEach((Integer x) -> messages.add(new Message("" + x, "payload: " + x, null))); + assertEquals(batchSize, messages.size()); + observableQueue.publish(messages); + + if (useWorkingChannel) { + verify(channel, times(batchSize)) + .basicPublish( + eq(name), + eq(routingKey), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } + } + + @Test + public void testGetMessagesFromExistingQueueAndDefaultConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromQueueAndDefaultConfiguration(channel, connection, true, true); + } + + @Test + public void testGetMessagesFromNotExistingQueueAndDefaultConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromQueueAndDefaultConfiguration(channel, connection, false, true); + } + + @Test + public void testGetMessagesFromQueueWithBadChannel() throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromQueueAndDefaultConfiguration(channel, connection, true, false); + } + + @Test(expected = RuntimeException.class) + public void testPublishMessagesToQueueWithBadChannel() throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testPublishMessagesToQueueAndDefaultConfiguration(channel, connection, true, false); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPObservalbleQueue_empty() throws IOException, TimeoutException { + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:test"); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + null, addresses, false, settings, retrySettings, batchSize, pollTimeMs); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPObservalbleQueue_addressEmpty() throws IOException, TimeoutException { + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:test"); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(mockGoodConnection(mockBaseChannel())), + null, + false, + settings, + retrySettings, + batchSize, + pollTimeMs); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPObservalbleQueue_settingsEmpty() throws IOException, TimeoutException { + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(mockGoodConnection(mockBaseChannel())), + addresses, + false, + null, + retrySettings, + batchSize, + pollTimeMs); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPObservalbleQueue_batchsizezero() throws IOException, TimeoutException { + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:test"); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(mockGoodConnection(mockBaseChannel())), + addresses, + false, + settings, + retrySettings, + 0, + pollTimeMs); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPObservalbleQueue_polltimezero() throws IOException, TimeoutException { + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:test"); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(mockGoodConnection(mockBaseChannel())), + addresses, + false, + settings, + retrySettings, + batchSize, + 0); + } + + @Test + public void testclosetExistingQueueAndDefaultConfiguration() + throws IOException, TimeoutException { + // Mock channel and connection + Channel channel = mockBaseChannel(); + Connection connection = mockGoodConnection(channel); + testGetMessagesFromQueueAndDefaultConfiguration_close(channel, connection, false, true); + } + + private void testGetMessagesFromQueueAndDefaultConfiguration( + Channel channel, Connection connection, boolean queueExists, boolean useWorkingChannel) + throws IOException, TimeoutException { + final Random random = new Random(); + + final String queueName = RandomStringUtils.randomAlphabetic(30); + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:" + queueName); + + List queue = buildQueue(random, batchSize); + channel = mockChannelForQueue(channel, useWorkingChannel, queueExists, queueName, queue); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + false, + settings, + retrySettings, + batchSize, + pollTimeMs); + + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE, observableQueue.getType()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE + ":" + queueName, observableQueue.getName()); + assertEquals(queueName, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + + runObserve(channel, observableQueue, queueName, useWorkingChannel, batchSize); + } + + private void testGetMessagesFromQueueAndDefaultConfiguration_close( + Channel channel, Connection connection, boolean queueExists, boolean useWorkingChannel) + throws IOException, TimeoutException { + final Random random = new Random(); + + final String queueName = RandomStringUtils.randomAlphabetic(30); + AMQPSettings settings = new AMQPSettings(properties).fromURI("amqp_queue:" + queueName); + + List queue = buildQueue(random, batchSize); + channel = mockChannelForQueue(channel, useWorkingChannel, queueExists, queueName, queue); + AMQPRetryPattern retrySettings = null; + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + false, + settings, + retrySettings, + batchSize, + pollTimeMs); + observableQueue.close(); + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE, observableQueue.getType()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE + ":" + queueName, observableQueue.getName()); + assertEquals(queueName, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + } + + private void testPublishMessagesToQueueAndDefaultConfiguration( + Channel channel, Connection connection, boolean queueExists, boolean useWorkingChannel) + throws IOException, TimeoutException { + final Random random = new Random(); + + final String queueName = RandomStringUtils.randomAlphabetic(30); + final AMQPSettings settings = + new AMQPSettings(properties) + .fromURI( + "amqp_queue:" + + queueName + + "?deliveryMode=2&durable=true&exclusive=false&autoDelete=true"); + assertTrue(settings.isDurable()); + assertFalse(settings.isExclusive()); + assertTrue(settings.autoDelete()); + assertEquals(2, settings.getDeliveryMode()); + + List queue = buildQueue(random, batchSize); + channel = mockChannelForQueue(channel, useWorkingChannel, queueExists, queueName, queue); + AMQPRetryPattern retrySettings = new AMQPRetryPattern(3, 5, RetryType.REGULARINTERVALS); + AMQPObservableQueue observableQueue = + new AMQPObservableQueue( + mockConnectionFactory(connection), + addresses, + false, + settings, + retrySettings, + batchSize, + pollTimeMs); + + assertArrayEquals(addresses, observableQueue.getAddresses()); + assertEquals(AMQPConstants.AMQP_QUEUE_TYPE, observableQueue.getType()); + assertEquals( + AMQPConstants.AMQP_QUEUE_TYPE + + ":" + + queueName + + "?deliveryMode=2&durable=true&exclusive=false&autoDelete=true", + observableQueue.getName()); + assertEquals(queueName, observableQueue.getURI()); + assertEquals(batchSize, observableQueue.getBatchSize()); + assertEquals(pollTimeMs, observableQueue.getPollTimeInMS()); + assertEquals(queue.size(), observableQueue.size()); + + List messages = new LinkedList<>(); + Observable.range(0, batchSize) + .forEach((Integer x) -> messages.add(new Message("" + x, "payload: " + x, null))); + assertEquals(batchSize, messages.size()); + observableQueue.publish(messages); + + if (useWorkingChannel) { + verify(channel, times(batchSize)) + .basicPublish( + eq(StringUtils.EMPTY), + eq(queueName), + any(AMQP.BasicProperties.class), + any(byte[].class)); + } + } +} diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java new file mode 100644 index 000000000..7b9f60e1f --- /dev/null +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.amqp; + +import java.time.Duration; + +import org.junit.Before; +import org.junit.Test; + +import com.netflix.conductor.contribs.queue.amqp.config.AMQPEventQueueProperties; +import com.netflix.conductor.contribs.queue.amqp.util.AMQPSettings; + +import com.rabbitmq.client.AMQP.PROTOCOL; +import com.rabbitmq.client.ConnectionFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AMQPSettingsTest { + + private AMQPEventQueueProperties properties; + + @Before + public void setUp() { + properties = mock(AMQPEventQueueProperties.class); + when(properties.getBatchSize()).thenReturn(1); + when(properties.getPollTimeDuration()).thenReturn(Duration.ofMillis(100)); + when(properties.getHosts()).thenReturn(ConnectionFactory.DEFAULT_HOST); + when(properties.getUsername()).thenReturn(ConnectionFactory.DEFAULT_USER); + when(properties.getPassword()).thenReturn(ConnectionFactory.DEFAULT_PASS); + when(properties.getVirtualHost()).thenReturn(ConnectionFactory.DEFAULT_VHOST); + when(properties.getPort()).thenReturn(PROTOCOL.PORT); + when(properties.getConnectionTimeoutInMilliSecs()).thenReturn(60000); + when(properties.isUseNio()).thenReturn(false); + when(properties.isDurable()).thenReturn(true); + when(properties.isExclusive()).thenReturn(false); + when(properties.isAutoDelete()).thenReturn(false); + when(properties.getContentType()).thenReturn("application/json"); + when(properties.getContentEncoding()).thenReturn("UTF-8"); + when(properties.getExchangeType()).thenReturn("topic"); + when(properties.getDeliveryMode()).thenReturn(2); + when(properties.isUseExchange()).thenReturn(true); + } + + @Test + public void testAMQPSettings_exchange_fromuri_defaultconfig() { + String exchangestring = + "amqp_exchange:myExchangeName?exchangeType=topic&routingKey=test&deliveryMode=2"; + AMQPSettings settings = new AMQPSettings(properties); + settings.fromURI(exchangestring); + assertEquals("topic", settings.getExchangeType()); + assertEquals("test", settings.getRoutingKey()); + assertEquals("myExchangeName", settings.getQueueOrExchangeName()); + } + + @Test + public void testAMQPSettings_queue_fromuri_defaultconfig() { + String exchangestring = + "amqp_queue:myQueueName?deliveryMode=2&durable=false&autoDelete=true&exclusive=true"; + AMQPSettings settings = new AMQPSettings(properties); + settings.fromURI(exchangestring); + assertFalse(settings.isDurable()); + assertTrue(settings.isExclusive()); + assertTrue(settings.autoDelete()); + assertEquals(2, settings.getDeliveryMode()); + assertEquals("myQueueName", settings.getQueueOrExchangeName()); + } + + @Test(expected = IllegalArgumentException.class) + public void testAMQPSettings_exchange_fromuri_wrongdeliverymode() { + String exchangestring = + "amqp_exchange:myExchangeName?exchangeType=topic&routingKey=test&deliveryMode=3"; + AMQPSettings settings = new AMQPSettings(properties); + settings.fromURI(exchangestring); + } +} diff --git a/dependencies.gradle b/dependencies.gradle index 63ae1a084..8f0faeb6b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -64,5 +64,8 @@ ext { revElasticSearch7 = '7.12.1' revCodec = '1.15' revAzureStorageBlobSdk = '12.7.0' + revNatsStreaming = '2.6.5' + revNats = '2.15.6' + revStan = '2.2.3' } diff --git a/nats-streaming/README.md b/nats-streaming/README.md new file mode 100644 index 000000000..d091fb006 --- /dev/null +++ b/nats-streaming/README.md @@ -0,0 +1,46 @@ +# Event Queue +## Published Artifacts + +Group: `com.netflix.conductor` + +| Published Artifact | Description | +| ----------- | ----------- | +| conductor-amqp | Support for integration with AMQP | +| conductor-nats | Support for integration with NATS | + +## Modules +### AMQP +https://www.amqp.org/ + +Provides ability to publish and consume messages from AMQP compatible message broker. + +#### Configuration +(Default values shown below) +```properties +conductor.event-queues.amqp.enabled=true +conductor.event-queues.amqp.hosts=localhost +conductor.event-queues.amqp.port=5672 +conductor.event-queues.amqp.username=guest +conductor.event-queues.amqp.password=guest +conductor.event-queues.amqp.virtualhost=/ +conductor.event-queues.amqp.useSslProtocol=false +#milliseconds +conductor.event-queues.amqp.connectionTimeout=60000 +conductor.event-queues.amqp.useExchange=true +conductor.event-queues.amqp.listenerQueuePrefix= +``` +For advanced configurations, see [AMQPEventQueueProperties](amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java) + +### NATS +https://nats.io/ + +Provides ability to publish and consume messages from NATS queues +#### Configuration +(Default values shown below) +```properties +conductor.event-queues.nats.enabled=true +conductor.event-queues.nats-stream.clusterId=test-cluster +conductor.event-queues.nats-stream.durableName= +conductor.event-queues.nats-stream.url=nats://localhost:4222 +conductor.event-queues.nats-stream.listenerQueuePrefix= +``` diff --git a/nats-streaming/build.gradle b/nats-streaming/build.gradle new file mode 100644 index 000000000..d517ea108 --- /dev/null +++ b/nats-streaming/build.gradle @@ -0,0 +1,14 @@ +dependencies { + implementation project(':conductor-common') + implementation project(':conductor-core') + + implementation "io.nats:java-nats-streaming:${revStan}" + implementation "io.nats:jnats:${revNatsStreaming}" + + implementation "org.apache.commons:commons-lang3:" + implementation "com.google.guava:guava:${revGuava}" + implementation "io.reactivex:rxjava:${revRxJava}" + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' +} \ No newline at end of file diff --git a/nats-streaming/dependencies.lock b/nats-streaming/dependencies.lock new file mode 100644 index 000000000..e0a43899f --- /dev/null +++ b/nats-streaming/dependencies.lock @@ -0,0 +1,189 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:java-nats-streaming": { + "locked": "2.2.3" + }, + "io.nats:jnats": { + "locked": "2.6.5" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:java-nats-streaming": { + "locked": "2.2.3" + }, + "io.nats:jnats": { + "locked": "2.6.5" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:java-nats-streaming": { + "locked": "2.2.3" + }, + "io.nats:jnats": { + "locked": "2.6.5" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + }, + "testRuntimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:java-nats-streaming": { + "locked": "2.2.3" + }, + "io.nats:jnats": { + "locked": "2.6.5" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + } +} \ No newline at end of file diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java new file mode 100644 index 000000000..738197cb5 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java @@ -0,0 +1,301 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import io.nats.client.NUID; +import rx.Observable; +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public abstract class NATSAbstractQueue implements ObservableQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSAbstractQueue.class); + protected LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + protected final Lock mu = new ReentrantLock(); + private final String queueType; + private ScheduledExecutorService execs; + private final Scheduler scheduler; + + protected final String queueURI; + protected final String subject; + protected String queue; + + // Indicates that observe was called (Event Handler) and we must to re-initiate subscription + // upon reconnection + private boolean observable; + private boolean isOpened; + private volatile boolean running; + + NATSAbstractQueue(String queueURI, String queueType, Scheduler scheduler) { + this.queueURI = queueURI; + this.queueType = queueType; + this.scheduler = scheduler; + + // If queue specified (e.g. subject:queue) - split to subject & queue + if (queueURI.contains(":")) { + this.subject = queueURI.substring(0, queueURI.indexOf(':')); + queue = queueURI.substring(queueURI.indexOf(':') + 1); + } else { + this.subject = queueURI; + queue = null; + } + LOGGER.info( + String.format( + "Initialized with queueURI=%s, subject=%s, queue=%s", + queueURI, subject, queue)); + } + + void onMessage(String subject, byte[] data) { + String payload = new String(data); + LOGGER.info(String.format("Received message for %s: %s", subject, payload)); + + Message dstMsg = new Message(); + dstMsg.setId(NUID.nextGlobal()); + dstMsg.setPayload(payload); + + messages.add(dstMsg); + } + + @Override + public Observable observe() { + LOGGER.info("Observe invoked for queueURI " + queueURI); + observable = true; + + mu.lock(); + try { + subscribe(); + } finally { + mu.unlock(); + } + + Observable.OnSubscribe onSubscribe = + subscriber -> { + Observable interval = + Observable.interval(100, TimeUnit.MILLISECONDS, scheduler); + interval.flatMap( + (Long x) -> { + if (!isRunning()) { + LOGGER.debug( + "Component stopped, skip listening for messages from NATS Queue"); + return Observable.from(Collections.emptyList()); + } else { + List available = new LinkedList<>(); + messages.drainTo(available); + + if (!available.isEmpty()) { + AtomicInteger count = new AtomicInteger(0); + StringBuilder buffer = new StringBuilder(); + available.forEach( + msg -> { + buffer.append(msg.getId()) + .append("=") + .append(msg.getPayload()); + count.incrementAndGet(); + + if (count.get() < available.size()) { + buffer.append(","); + } + }); + LOGGER.info( + String.format( + "Batch from %s to conductor is %s", + subject, buffer.toString())); + } + + return Observable.from(available); + } + }) + .subscribe(subscriber::onNext, subscriber::onError); + }; + return Observable.create(onSubscribe); + } + + @Override + public String getType() { + return queueType; + } + + @Override + public String getName() { + return queueURI; + } + + @Override + public String getURI() { + return queueURI; + } + + @Override + public List ack(List messages) { + return Collections.emptyList(); + } + + @Override + public void setUnackTimeout(Message message, long unackTimeout) {} + + @Override + public long size() { + return messages.size(); + } + + @Override + public void publish(List messages) { + messages.forEach( + message -> { + try { + String payload = message.getPayload(); + publish(subject, payload.getBytes()); + LOGGER.info(String.format("Published message to %s: %s", subject, payload)); + } catch (Exception ex) { + LOGGER.error( + "Failed to publish message " + + message.getPayload() + + " to " + + subject, + ex); + throw new RuntimeException(ex); + } + }); + } + + @Override + public boolean rePublishIfNoAck() { + return true; + } + + @Override + public void close() { + LOGGER.info("Closing connection for " + queueURI); + mu.lock(); + try { + if (execs != null) { + execs.shutdownNow(); + execs = null; + } + closeSubs(); + closeConn(); + isOpened = false; + } finally { + mu.unlock(); + } + } + + public void open() { + // do nothing if not closed + if (isOpened) { + return; + } + + mu.lock(); + try { + try { + connect(); + + // Re-initiated subscription if existed + if (observable) { + subscribe(); + } + } catch (Exception ignore) { + } + + execs = Executors.newScheduledThreadPool(1); + execs.scheduleAtFixedRate(this::monitor, 0, 500, TimeUnit.MILLISECONDS); + isOpened = true; + } finally { + mu.unlock(); + } + } + + private void monitor() { + if (isConnected()) { + return; + } + + LOGGER.error("Monitor invoked for " + queueURI); + mu.lock(); + try { + closeSubs(); + closeConn(); + + // Connect + connect(); + + // Re-initiated subscription if existed + if (observable) { + subscribe(); + } + } catch (Exception ex) { + LOGGER.error("Monitor failed with " + ex.getMessage() + " for " + queueURI, ex); + } finally { + mu.unlock(); + } + } + + public boolean isClosed() { + return !isOpened; + } + + void ensureConnected() { + if (!isConnected()) { + throw new RuntimeException("No nats connection"); + } + } + + @Override + public void start() { + LOGGER.info("Started listening to {}:{}", getClass().getSimpleName(), queueURI); + running = true; + } + + @Override + public void stop() { + LOGGER.info("Stopped listening to {}:{}", getClass().getSimpleName(), queueURI); + running = false; + } + + @Override + public boolean isRunning() { + return running; + } + + abstract void connect(); + + abstract boolean isConnected(); + + abstract void publish(String subject, byte[] data) throws Exception; + + abstract void subscribe(); + + abstract void closeSubs(); + + abstract void closeConn(); +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java new file mode 100644 index 000000000..e2d758157 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java @@ -0,0 +1,114 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Subscription; +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSObservableQueue extends NATSAbstractQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSObservableQueue.class); + private Subscription subs; + private Connection conn; + + public NATSObservableQueue(String queueURI, Scheduler scheduler) { + super(queueURI, "nats", scheduler); + open(); + } + + @Override + public boolean isConnected() { + return (conn != null && Connection.Status.CONNECTED.equals(conn.getStatus())); + } + + @Override + public void connect() { + try { + Connection temp = Nats.connect(); + LOGGER.info("Successfully connected for " + queueURI); + conn = temp; + } catch (Exception e) { + LOGGER.error("Unable to establish nats connection for " + queueURI, e); + throw new RuntimeException(e); + } + } + + @Override + public void subscribe() { + // do nothing if already subscribed + if (subs != null) { + return; + } + + try { + ensureConnected(); + // Create subject/queue subscription if the queue has been provided + if (StringUtils.isNotEmpty(queue)) { + LOGGER.info( + "No subscription. Creating a queue subscription. subject={}, queue={}", + subject, + queue); + conn.createDispatcher(msg -> onMessage(msg.getSubject(), msg.getData())); + subs = conn.subscribe(subject, queue); + } else { + LOGGER.info( + "No subscription. Creating a pub/sub subscription. subject={}", subject); + conn.createDispatcher(msg -> onMessage(msg.getSubject(), msg.getData())); + subs = conn.subscribe(subject); + } + } catch (Exception ex) { + LOGGER.error( + "Subscription failed with " + ex.getMessage() + " for queueURI " + queueURI, + ex); + } + } + + @Override + public void publish(String subject, byte[] data) throws Exception { + ensureConnected(); + conn.publish(subject, data); + } + + @Override + public void closeSubs() { + if (subs != null) { + try { + subs.unsubscribe(); + } catch (Exception ex) { + LOGGER.error("closeSubs failed with " + ex.getMessage() + " for " + queueURI, ex); + } + subs = null; + } + } + + @Override + public void closeConn() { + if (conn != null) { + try { + conn.close(); + } catch (Exception ex) { + LOGGER.error("closeConn failed with " + ex.getMessage() + " for " + queueURI, ex); + } + conn = null; + } + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java new file mode 100644 index 000000000..5f3c17e68 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java @@ -0,0 +1,144 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan; + +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.nats.client.Connection; +import io.nats.streaming.*; +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSStreamObservableQueue extends NATSAbstractQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSStreamObservableQueue.class); + private final StreamingConnectionFactory fact; + private StreamingConnection conn; + private Subscription subs; + private final String durableName; + + public NATSStreamObservableQueue( + String clusterId, + String natsUrl, + String durableName, + String queueURI, + Scheduler scheduler) { + super(queueURI, "nats_stream", scheduler); + Options.Builder options = new Options.Builder(); + options.clusterId(clusterId); + options.clientId(UUID.randomUUID().toString()); + options.natsUrl(natsUrl); + this.fact = new StreamingConnectionFactory(options.build()); + this.durableName = durableName; + open(); + } + + @Override + public boolean isConnected() { + return (conn != null + && conn.getNatsConnection() != null + && Connection.Status.CONNECTED.equals(conn.getNatsConnection().getStatus())); + } + + @Override + public void connect() { + try { + StreamingConnection temp = fact.createConnection(); + LOGGER.info("Successfully connected for " + queueURI); + conn = temp; + } catch (Exception e) { + LOGGER.error("Unable to establish nats streaming connection for " + queueURI, e); + throw new RuntimeException(e); + } + } + + @Override + public void subscribe() { + // do nothing if already subscribed + if (subs != null) { + return; + } + + try { + ensureConnected(); + SubscriptionOptions subscriptionOptions = + new SubscriptionOptions.Builder().durableName(durableName).build(); + // Create subject/queue subscription if the queue has been provided + if (StringUtils.isNotEmpty(queue)) { + LOGGER.info( + "No subscription. Creating a queue subscription. subject={}, queue={}", + subject, + queue); + subs = + conn.subscribe( + subject, + queue, + msg -> onMessage(msg.getSubject(), msg.getData()), + subscriptionOptions); + } else { + LOGGER.info( + "No subscription. Creating a pub/sub subscription. subject={}", subject); + subs = + conn.subscribe( + subject, + msg -> onMessage(msg.getSubject(), msg.getData()), + subscriptionOptions); + } + } catch (Exception ex) { + LOGGER.error( + "Subscription failed with " + ex.getMessage() + " for queueURI " + queueURI, + ex); + } + } + + @Override + public void publish(String subject, byte[] data) throws Exception { + ensureConnected(); + conn.publish(subject, data); + } + + @Override + public void closeSubs() { + if (subs != null) { + try { + subs.close(true); + } catch (Exception ex) { + LOGGER.error("closeSubs failed with " + ex.getMessage() + " for " + queueURI, ex); + } + subs = null; + } + } + + @Override + public void closeConn() { + if (conn != null) { + try { + conn.close(); + } catch (Exception ex) { + LOGGER.error("closeConn failed with " + ex.getMessage() + " for " + queueURI, ex); + } + conn = null; + } + } + + @Override + public boolean rePublishIfNoAck() { + return false; + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java new file mode 100644 index 000000000..2b1b8a767 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.netflix.conductor.core.events.EventQueueProvider; + +import rx.Scheduler; + +@Configuration +@ConditionalOnProperty(name = "conductor.event-queues.nats.enabled", havingValue = "true") +public class NATSConfiguration { + + @Bean + public EventQueueProvider natsEventQueueProvider(Environment environment, Scheduler scheduler) { + return new NATSEventQueueProvider(environment, scheduler); + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java new file mode 100644 index 000000000..0bee790d4 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; + +import com.netflix.conductor.contribs.queue.stan.NATSObservableQueue; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSEventQueueProvider implements EventQueueProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSEventQueueProvider.class); + + protected Map queues = new ConcurrentHashMap<>(); + private final Scheduler scheduler; + + public NATSEventQueueProvider(Environment environment, Scheduler scheduler) { + this.scheduler = scheduler; + LOGGER.info("NATS Event Queue Provider initialized..."); + } + + @Override + public String getQueueType() { + return "nats"; + } + + @Override + @NonNull + public ObservableQueue getQueue(String queueURI) { + NATSObservableQueue queue = + queues.computeIfAbsent(queueURI, q -> new NATSObservableQueue(queueURI, scheduler)); + if (queue.isClosed()) { + queue.open(); + } + return queue; + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java new file mode 100644 index 000000000..492da53a4 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan.config; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.contribs.queue.stan.NATSStreamObservableQueue; +import com.netflix.conductor.core.config.ConductorProperties; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; +import com.netflix.conductor.model.TaskModel; + +import rx.Scheduler; + +@Configuration +@EnableConfigurationProperties(NATSStreamProperties.class) +@ConditionalOnProperty(name = "conductor.event-queues.nats-stream.enabled", havingValue = "true") +public class NATSStreamConfiguration { + + @Bean + public EventQueueProvider natsEventQueueProvider( + NATSStreamProperties properties, Scheduler scheduler) { + return new NATSStreamEventQueueProvider(properties, scheduler); + } + + @ConditionalOnProperty(name = "conductor.default-event-queue.type", havingValue = "nats_stream") + @Bean + public Map getQueues( + ConductorProperties conductorProperties, + NATSStreamProperties properties, + Scheduler scheduler) { + String stack = ""; + if (conductorProperties.getStack() != null && conductorProperties.getStack().length() > 0) { + stack = conductorProperties.getStack() + "_"; + } + TaskModel.Status[] statuses = + new TaskModel.Status[] {TaskModel.Status.COMPLETED, TaskModel.Status.FAILED}; + Map queues = new HashMap<>(); + for (TaskModel.Status status : statuses) { + String queuePrefix = + StringUtils.isBlank(properties.getListenerQueuePrefix()) + ? conductorProperties.getAppId() + "_nats_stream_notify_" + stack + : properties.getListenerQueuePrefix(); + + String queueName = queuePrefix + status.name() + getQueueGroup(properties); + + ObservableQueue queue = + new NATSStreamObservableQueue( + properties.getClusterId(), + properties.getUrl(), + properties.getDurableName(), + queueName, + scheduler); + queues.put(status, queue); + } + + return queues; + } + + private String getQueueGroup(final NATSStreamProperties properties) { + if (properties.getDefaultQueueGroup() == null + || properties.getDefaultQueueGroup().isBlank()) { + return ""; + } + return ":" + properties.getDefaultQueueGroup(); + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java new file mode 100644 index 000000000..29de21e7b --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.NonNull; + +import com.netflix.conductor.contribs.queue.stan.NATSStreamObservableQueue; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSStreamEventQueueProvider implements EventQueueProvider { + + private static final Logger LOGGER = + LoggerFactory.getLogger(NATSStreamEventQueueProvider.class); + protected final Map queues = new ConcurrentHashMap<>(); + private final String durableName; + private final String clusterId; + private final String natsUrl; + private final Scheduler scheduler; + + public NATSStreamEventQueueProvider(NATSStreamProperties properties, Scheduler scheduler) { + LOGGER.info("NATS Stream Event Queue Provider init"); + this.scheduler = scheduler; + + // Get NATS Streaming options + clusterId = properties.getClusterId(); + durableName = properties.getDurableName(); + natsUrl = properties.getUrl(); + + LOGGER.info( + "NATS Streaming clusterId=" + + clusterId + + ", natsUrl=" + + natsUrl + + ", durableName=" + + durableName); + LOGGER.info("NATS Stream Event Queue Provider initialized..."); + } + + @Override + public String getQueueType() { + return "nats_stream"; + } + + @Override + @NonNull + public ObservableQueue getQueue(String queueURI) { + NATSStreamObservableQueue queue = + queues.computeIfAbsent( + queueURI, + q -> + new NATSStreamObservableQueue( + clusterId, natsUrl, durableName, queueURI, scheduler)); + if (queue.isClosed()) { + queue.open(); + } + return queue; + } +} diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java new file mode 100644 index 000000000..459ac8723 --- /dev/null +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.stan.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import io.nats.client.Options; + +@ConfigurationProperties("conductor.event-queues.nats-stream") +public class NATSStreamProperties { + + /** The cluster id of the STAN session */ + private String clusterId = "test-cluster"; + + /** The durable subscriber name for the subscription */ + private String durableName = null; + + /** The NATS connection url */ + private String url = Options.DEFAULT_URL; + + /** The prefix to be used for the default listener queues */ + private String listenerQueuePrefix = ""; + + /** WAIT tasks default queue group, to make subscription round-robin delivery to single sub */ + private String defaultQueueGroup = "wait-group"; + + public String getClusterId() { + return clusterId; + } + + public void setClusterId(String clusterId) { + this.clusterId = clusterId; + } + + public String getDurableName() { + return durableName; + } + + public void setDurableName(String durableName) { + this.durableName = durableName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getListenerQueuePrefix() { + return listenerQueuePrefix; + } + + public void setListenerQueuePrefix(String listenerQueuePrefix) { + this.listenerQueuePrefix = listenerQueuePrefix; + } + + public String getDefaultQueueGroup() { + return defaultQueueGroup; + } + + public void setDefaultQueueGroup(String defaultQueueGroup) { + this.defaultQueueGroup = defaultQueueGroup; + } +} diff --git a/nats/build.gradle b/nats/build.gradle new file mode 100644 index 000000000..fa8741e40 --- /dev/null +++ b/nats/build.gradle @@ -0,0 +1,13 @@ +dependencies { + implementation project(':conductor-common') + implementation project(':conductor-core') + + implementation "io.nats:jnats:${revNats}" + + implementation "org.apache.commons:commons-lang3:" + implementation "com.google.guava:guava:${revGuava}" + implementation "io.reactivex:rxjava:${revRxJava}" + + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' +} \ No newline at end of file diff --git a/nats/dependencies.lock b/nats/dependencies.lock new file mode 100644 index 000000000..caa98cf44 --- /dev/null +++ b/nats/dependencies.lock @@ -0,0 +1,177 @@ +{ + "annotationProcessor": { + "org.springframework.boot:spring-boot-configuration-processor": { + "locked": "2.7.16" + } + }, + "compileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:jnats": { + "locked": "2.15.6" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.springframework.boot:spring-boot-starter": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-web": { + "locked": "2.7.16" + } + }, + "runtimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:jnats": { + "locked": "2.15.6" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + } + }, + "testCompileClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:jnats": { + "locked": "2.15.6" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + }, + "testRuntimeClasspath": { + "com.google.guava:guava": { + "locked": "32.1.2-jre" + }, + "com.netflix.conductor:conductor-common": { + "locked": "3.15.0" + }, + "com.netflix.conductor:conductor-core": { + "locked": "3.15.0" + }, + "io.nats:jnats": { + "locked": "2.15.6" + }, + "io.reactivex:rxjava": { + "locked": "1.2.2" + }, + "junit:junit": { + "locked": "4.13.2" + }, + "org.apache.commons:commons-lang3": { + "locked": "3.12.0" + }, + "org.apache.logging.log4j:log4j-api": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-core": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-jul": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-slf4j-impl": { + "locked": "2.17.2" + }, + "org.apache.logging.log4j:log4j-web": { + "locked": "2.17.2" + }, + "org.junit.vintage:junit-vintage-engine": { + "locked": "5.8.2" + }, + "org.springframework.boot:spring-boot-starter-log4j2": { + "locked": "2.7.16" + }, + "org.springframework.boot:spring-boot-starter-test": { + "locked": "2.7.16" + } + } +} \ No newline at end of file diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java new file mode 100644 index 000000000..a5091cbdb --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java @@ -0,0 +1,285 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.contribs.queue.nats.config.JetStreamProperties; +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import io.nats.client.Connection; +import io.nats.client.ConnectionListener; +import io.nats.client.JetStream; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.JetStreamSubscription; +import io.nats.client.Nats; +import io.nats.client.Options; +import io.nats.client.PushSubscribeOptions; +import io.nats.client.api.RetentionPolicy; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.StreamInfo; +import rx.Observable; +import rx.Scheduler; + +/** + * @author andrey.stelmashenko@gmail.com + */ +public class JetStreamObservableQueue implements ObservableQueue { + private static final Logger LOG = LoggerFactory.getLogger(JetStreamObservableQueue.class); + private final LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + private final Lock mu = new ReentrantLock(); + private final String queueType; + private final String subject; + private final String queueUri; + private final JetStreamProperties properties; + private final Scheduler scheduler; + private final AtomicBoolean running = new AtomicBoolean(false); + private Connection nc; + private JetStreamSubscription sub; + private Observable interval; + private final String queueGroup; + + public JetStreamObservableQueue( + JetStreamProperties properties, + String queueType, + String queueUri, + Scheduler scheduler) { + LOG.debug("JSM obs queue create, qtype={}, quri={}", queueType, queueUri); + + this.queueUri = queueUri; + // If queue specified (e.g. subject:queue) - split to subject & queue + if (queueUri.contains(":")) { + this.subject = queueUri.substring(0, queueUri.indexOf(':')); + queueGroup = queueUri.substring(queueUri.indexOf(':') + 1); + } else { + this.subject = queueUri; + queueGroup = null; + } + + this.queueType = queueType; + this.properties = properties; + this.scheduler = scheduler; + } + + @Override + public Observable observe() { + return Observable.create(getOnSubscribe()); + } + + private Observable.OnSubscribe getOnSubscribe() { + return subscriber -> { + interval = + Observable.interval( + properties.getPollTimeDuration().toMillis(), + TimeUnit.MILLISECONDS, + scheduler); + interval.flatMap( + (Long x) -> { + if (!this.isRunning()) { + LOG.debug( + "Component stopped, skip listening for messages from JSM Queue '{}'", + subject); + return Observable.from(Collections.emptyList()); + } else { + List available = new ArrayList<>(); + messages.drainTo(available); + if (!available.isEmpty()) { + LOG.debug( + "Processing JSM queue '{}' batch messages count={}", + subject, + available.size()); + } + return Observable.from(available); + } + }) + .subscribe(subscriber::onNext, subscriber::onError); + }; + } + + @Override + public String getType() { + return queueType; + } + + @Override + public String getName() { + return queueUri; + } + + @Override + public String getURI() { + return getName(); + } + + @Override + public List ack(List messages) { + messages.forEach(m -> ((JsmMessage) m).getJsmMsg().ack()); + return Collections.emptyList(); + } + + @Override + public void publish(List messages) { + try (Connection conn = Nats.connect(properties.getUrl())) { + JetStream js = conn.jetStream(); + for (Message msg : messages) { + js.publish(subject, msg.getPayload().getBytes()); + } + } catch (IOException | JetStreamApiException e) { + throw new NatsException("Failed to publish to jsm", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new NatsException("Failed to publish to jsm", e); + } + } + + @Override + public void setUnackTimeout(Message message, long unackTimeout) { + // do nothing, not supported + } + + @Override + public long size() { + try { + return sub.getConsumerInfo().getNumPending(); + } catch (IOException | JetStreamApiException e) { + LOG.warn("Failed to get stream '{}' info", subject); + } + return 0; + } + + @Override + public void start() { + mu.lock(); + try { + natsConnect(); + } finally { + mu.unlock(); + } + } + + @Override + public void stop() { + interval.unsubscribeOn(scheduler); + try { + if (nc != null) { + nc.close(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOG.error("Failed to close Nats connection", e); + } + running.set(false); + } + + @Override + public boolean isRunning() { + return this.running.get(); + } + + private void natsConnect() { + if (running.get()) { + return; + } + LOG.info("Starting JSM observable, name={}", queueUri); + try { + Nats.connectAsynchronously( + new Options.Builder() + .connectionListener( + (conn, type) -> { + LOG.info("Connection to JSM updated: {}", type); + this.nc = conn; + subscribeOnce(conn, type); + }) + .server(properties.getUrl()) + .maxReconnects(-1) + .build(), + true); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new NatsException("Failed to connect to JSM", e); + } + } + + private void createStream(Connection nc) { + JetStreamManagement jsm; + try { + jsm = nc.jetStreamManagement(); + } catch (IOException e) { + throw new NatsException("Failed to get jsm management", e); + } + + StreamConfiguration streamConfig = + StreamConfiguration.builder() + .name(subject) + .retentionPolicy(RetentionPolicy.WorkQueue) + .storageType(StorageType.get(properties.getStreamStorageType())) + .build(); + + try { + StreamInfo streamInfo = jsm.addStream(streamConfig); + LOG.debug("Create stream, info: {}", streamInfo); + } catch (IOException | JetStreamApiException e) { + LOG.error("Failed to add stream: " + streamConfig, e); + } + } + + private void subscribeOnce(Connection nc, ConnectionListener.Events type) { + if (type.equals(ConnectionListener.Events.CONNECTED) + || type.equals(ConnectionListener.Events.RECONNECTED)) { + createStream(nc); + subscribe(nc); + } + } + + private void subscribe(Connection nc) { + try { + JetStream js = nc.jetStream(); + + PushSubscribeOptions pso = + PushSubscribeOptions.builder().durable(properties.getDurableName()).build(); + LOG.debug("Subscribing jsm, subject={}, options={}", subject, pso); + sub = + js.subscribe( + subject, + queueGroup, + nc.createDispatcher(), + msg -> { + var message = new JsmMessage(); + message.setJsmMsg(msg); + message.setId(msg.getSID()); + message.setPayload(new String(msg.getData())); + messages.add(message); + }, + /*autoAck*/ false, + pso); + LOG.debug("Subscribed successfully {}", sub.getConsumerInfo()); + this.running.set(true); + } catch (IOException | JetStreamApiException e) { + LOG.error("Failed to subscribe", e); + } + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java new file mode 100644 index 000000000..49fb591c5 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats; + +import com.netflix.conductor.core.events.queue.Message; + +/** + * @author andrey.stelmashenko@gmail.com + */ +public class JsmMessage extends Message { + private io.nats.client.Message jsmMsg; + + public io.nats.client.Message getJsmMsg() { + return jsmMsg; + } + + public void setJsmMsg(io.nats.client.Message jsmMsg) { + this.jsmMsg = jsmMsg; + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java new file mode 100644 index 000000000..1d4b839c4 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java @@ -0,0 +1,301 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.core.events.queue.Message; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import io.nats.client.NUID; +import rx.Observable; +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public abstract class NATSAbstractQueue implements ObservableQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSAbstractQueue.class); + protected LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + protected final Lock mu = new ReentrantLock(); + private final String queueType; + private ScheduledExecutorService execs; + private final Scheduler scheduler; + + protected final String queueURI; + protected final String subject; + protected String queue; + + // Indicates that observe was called (Event Handler) and we must to re-initiate subscription + // upon reconnection + private boolean observable; + private boolean isOpened; + private volatile boolean running; + + NATSAbstractQueue(String queueURI, String queueType, Scheduler scheduler) { + this.queueURI = queueURI; + this.queueType = queueType; + this.scheduler = scheduler; + + // If queue specified (e.g. subject:queue) - split to subject & queue + if (queueURI.contains(":")) { + this.subject = queueURI.substring(0, queueURI.indexOf(':')); + queue = queueURI.substring(queueURI.indexOf(':') + 1); + } else { + this.subject = queueURI; + queue = null; + } + LOGGER.info( + String.format( + "Initialized with queueURI=%s, subject=%s, queue=%s", + queueURI, subject, queue)); + } + + void onMessage(String subject, byte[] data) { + String payload = new String(data); + LOGGER.info(String.format("Received message for %s: %s", subject, payload)); + + Message dstMsg = new Message(); + dstMsg.setId(NUID.nextGlobal()); + dstMsg.setPayload(payload); + + messages.add(dstMsg); + } + + @Override + public Observable observe() { + LOGGER.info("Observe invoked for queueURI " + queueURI); + observable = true; + + mu.lock(); + try { + subscribe(); + } finally { + mu.unlock(); + } + + Observable.OnSubscribe onSubscribe = + subscriber -> { + Observable interval = + Observable.interval(100, TimeUnit.MILLISECONDS, scheduler); + interval.flatMap( + (Long x) -> { + if (!isRunning()) { + LOGGER.debug( + "Component stopped, skip listening for messages from NATS Queue"); + return Observable.from(Collections.emptyList()); + } else { + List available = new LinkedList<>(); + messages.drainTo(available); + + if (!available.isEmpty()) { + AtomicInteger count = new AtomicInteger(0); + StringBuilder buffer = new StringBuilder(); + available.forEach( + msg -> { + buffer.append(msg.getId()) + .append("=") + .append(msg.getPayload()); + count.incrementAndGet(); + + if (count.get() < available.size()) { + buffer.append(","); + } + }); + LOGGER.info( + String.format( + "Batch from %s to conductor is %s", + subject, buffer.toString())); + } + + return Observable.from(available); + } + }) + .subscribe(subscriber::onNext, subscriber::onError); + }; + return Observable.create(onSubscribe); + } + + @Override + public String getType() { + return queueType; + } + + @Override + public String getName() { + return queueURI; + } + + @Override + public String getURI() { + return queueURI; + } + + @Override + public List ack(List messages) { + return Collections.emptyList(); + } + + @Override + public void setUnackTimeout(Message message, long unackTimeout) {} + + @Override + public long size() { + return messages.size(); + } + + @Override + public void publish(List messages) { + messages.forEach( + message -> { + try { + String payload = message.getPayload(); + publish(subject, payload.getBytes()); + LOGGER.info(String.format("Published message to %s: %s", subject, payload)); + } catch (Exception ex) { + LOGGER.error( + "Failed to publish message " + + message.getPayload() + + " to " + + subject, + ex); + throw new RuntimeException(ex); + } + }); + } + + @Override + public boolean rePublishIfNoAck() { + return true; + } + + @Override + public void close() { + LOGGER.info("Closing connection for " + queueURI); + mu.lock(); + try { + if (execs != null) { + execs.shutdownNow(); + execs = null; + } + closeSubs(); + closeConn(); + isOpened = false; + } finally { + mu.unlock(); + } + } + + public void open() { + // do nothing if not closed + if (isOpened) { + return; + } + + mu.lock(); + try { + try { + connect(); + + // Re-initiated subscription if existed + if (observable) { + subscribe(); + } + } catch (Exception ignore) { + } + + execs = Executors.newScheduledThreadPool(1); + execs.scheduleAtFixedRate(this::monitor, 0, 500, TimeUnit.MILLISECONDS); + isOpened = true; + } finally { + mu.unlock(); + } + } + + private void monitor() { + if (isConnected()) { + return; + } + + LOGGER.error("Monitor invoked for " + queueURI); + mu.lock(); + try { + closeSubs(); + closeConn(); + + // Connect + connect(); + + // Re-initiated subscription if existed + if (observable) { + subscribe(); + } + } catch (Exception ex) { + LOGGER.error("Monitor failed with " + ex.getMessage() + " for " + queueURI, ex); + } finally { + mu.unlock(); + } + } + + public boolean isClosed() { + return !isOpened; + } + + void ensureConnected() { + if (!isConnected()) { + throw new RuntimeException("No nats connection"); + } + } + + @Override + public void start() { + LOGGER.info("Started listening to {}:{}", getClass().getSimpleName(), queueURI); + running = true; + } + + @Override + public void stop() { + LOGGER.info("Stopped listening to {}:{}", getClass().getSimpleName(), queueURI); + running = false; + } + + @Override + public boolean isRunning() { + return running; + } + + abstract void connect(); + + abstract boolean isConnected(); + + abstract void publish(String subject, byte[] data) throws Exception; + + abstract void subscribe(); + + abstract void closeSubs(); + + abstract void closeConn(); +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java new file mode 100644 index 000000000..51750fc51 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java @@ -0,0 +1,114 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Subscription; +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSObservableQueue extends NATSAbstractQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSObservableQueue.class); + private Subscription subs; + private Connection conn; + + public NATSObservableQueue(String queueURI, Scheduler scheduler) { + super(queueURI, "nats", scheduler); + open(); + } + + @Override + public boolean isConnected() { + return (conn != null && Connection.Status.CONNECTED.equals(conn.getStatus())); + } + + @Override + public void connect() { + try { + Connection temp = Nats.connect(); + LOGGER.info("Successfully connected for " + queueURI); + conn = temp; + } catch (Exception e) { + LOGGER.error("Unable to establish nats connection for " + queueURI, e); + throw new RuntimeException(e); + } + } + + @Override + public void subscribe() { + // do nothing if already subscribed + if (subs != null) { + return; + } + + try { + ensureConnected(); + // Create subject/queue subscription if the queue has been provided + if (StringUtils.isNotEmpty(queue)) { + LOGGER.info( + "No subscription. Creating a queue subscription. subject={}, queue={}", + subject, + queue); + conn.createDispatcher(msg -> onMessage(msg.getSubject(), msg.getData())); + subs = conn.subscribe(subject, queue); + } else { + LOGGER.info( + "No subscription. Creating a pub/sub subscription. subject={}", subject); + conn.createDispatcher(msg -> onMessage(msg.getSubject(), msg.getData())); + subs = conn.subscribe(subject); + } + } catch (Exception ex) { + LOGGER.error( + "Subscription failed with " + ex.getMessage() + " for queueURI " + queueURI, + ex); + } + } + + @Override + public void publish(String subject, byte[] data) throws Exception { + ensureConnected(); + conn.publish(subject, data); + } + + @Override + public void closeSubs() { + if (subs != null) { + try { + subs.unsubscribe(); + } catch (Exception ex) { + LOGGER.error("closeSubs failed with " + ex.getMessage() + " for " + queueURI, ex); + } + subs = null; + } + } + + @Override + public void closeConn() { + if (conn != null) { + try { + conn.close(); + } catch (Exception ex) { + LOGGER.error("closeConn failed with " + ex.getMessage() + " for " + queueURI, ex); + } + conn = null; + } + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java new file mode 100644 index 000000000..9120d986a --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats; + +public class NatsException extends RuntimeException { + public NatsException() { + super(); + } + + public NatsException(String message) { + super(message); + } + + public NatsException(String message, Throwable cause) { + super(message, cause); + } + + public NatsException(Throwable cause) { + super(cause); + } + + protected NatsException( + String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java new file mode 100644 index 000000000..f0d26d285 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats.config; + +import java.util.EnumMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.conductor.core.config.ConductorProperties; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; +import com.netflix.conductor.model.TaskModel; + +import rx.Scheduler; + +/** + * @author andrey.stelmashenko@gmail.com + */ +@Configuration +@EnableConfigurationProperties(JetStreamProperties.class) +@ConditionalOnProperty(name = "conductor.event-queues.jsm.enabled", havingValue = "true") +public class JetStreamConfiguration { + @Bean + public EventQueueProvider jsmEventQueueProvider( + JetStreamProperties properties, Scheduler scheduler) { + return new JetStreamEventQueueProvider(properties, scheduler); + } + + @ConditionalOnProperty(name = "conductor.default-event-queue.type", havingValue = "jsm") + @Bean + public Map getQueues( + JetStreamEventQueueProvider provider, + ConductorProperties conductorProperties, + JetStreamProperties properties) { + String stack = ""; + if (conductorProperties.getStack() != null && conductorProperties.getStack().length() > 0) { + stack = conductorProperties.getStack() + "_"; + } + TaskModel.Status[] statuses = + new TaskModel.Status[] {TaskModel.Status.COMPLETED, TaskModel.Status.FAILED}; + Map queues = new EnumMap<>(TaskModel.Status.class); + for (TaskModel.Status status : statuses) { + String queuePrefix = + StringUtils.isBlank(properties.getListenerQueuePrefix()) + ? conductorProperties.getAppId() + "_jsm_notify_" + stack + : properties.getListenerQueuePrefix(); + + String queueName = queuePrefix + status.name() + getQueueGroup(properties); + + ObservableQueue queue = provider.getQueue(queueName); + queues.put(status, queue); + } + + return queues; + } + + private String getQueueGroup(final JetStreamProperties properties) { + if (properties.getDefaultQueueGroup() == null + || properties.getDefaultQueueGroup().isBlank()) { + return ""; + } + return ":" + properties.getDefaultQueueGroup(); + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java new file mode 100644 index 000000000..b6bc20a60 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.NonNull; + +import com.netflix.conductor.contribs.queue.nats.JetStreamObservableQueue; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import rx.Scheduler; + +/** + * @author andrey.stelmashenko@gmail.com + */ +public class JetStreamEventQueueProvider implements EventQueueProvider { + public static final String QUEUE_TYPE = "jsm"; + private static final Logger LOG = LoggerFactory.getLogger(JetStreamEventQueueProvider.class); + private final Map queues = new ConcurrentHashMap<>(); + private final JetStreamProperties properties; + private final Scheduler scheduler; + + public JetStreamEventQueueProvider(JetStreamProperties properties, Scheduler scheduler) { + LOG.info("NATS Event Queue Provider initialized..."); + this.properties = properties; + this.scheduler = scheduler; + } + + @Override + public String getQueueType() { + return QUEUE_TYPE; + } + + @Override + @NonNull + public ObservableQueue getQueue(String queueURI) throws IllegalArgumentException { + LOG.debug("Getting obs queue, quri={}", queueURI); + return queues.computeIfAbsent( + queueURI, + q -> new JetStreamObservableQueue(properties, getQueueType(), queueURI, scheduler)); + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java new file mode 100644 index 000000000..0f4feff1b --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java @@ -0,0 +1,88 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats.config; + +import java.time.Duration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import io.nats.client.Options; + +/** + * @author andrey.stelmashenko@gmail.com + */ +@ConfigurationProperties("conductor.event-queues.jsm") +public class JetStreamProperties { + private String listenerQueuePrefix = ""; + + /** The durable subscriber name for the subscription */ + private String durableName = "defaultQueue"; + + private String streamStorageType = "file"; + + /** The NATS connection url */ + private String url = Options.DEFAULT_URL; + + private Duration pollTimeDuration = Duration.ofMillis(100); + + /** WAIT tasks default queue group, to make subscription round-robin delivery to single sub */ + private String defaultQueueGroup = "wait-group"; + + public Duration getPollTimeDuration() { + return pollTimeDuration; + } + + public void setPollTimeDuration(Duration pollTimeDuration) { + this.pollTimeDuration = pollTimeDuration; + } + + public String getListenerQueuePrefix() { + return listenerQueuePrefix; + } + + public void setListenerQueuePrefix(String listenerQueuePrefix) { + this.listenerQueuePrefix = listenerQueuePrefix; + } + + public String getDurableName() { + return durableName; + } + + public void setDurableName(String durableName) { + this.durableName = durableName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getStreamStorageType() { + return streamStorageType; + } + + public void setStreamStorageType(String streamStorageType) { + this.streamStorageType = streamStorageType; + } + + public String getDefaultQueueGroup() { + return defaultQueueGroup; + } + + public void setDefaultQueueGroup(String defaultQueueGroup) { + this.defaultQueueGroup = defaultQueueGroup; + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java new file mode 100644 index 000000000..cc3e7de90 --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.netflix.conductor.core.events.EventQueueProvider; + +import rx.Scheduler; + +@Configuration +@ConditionalOnProperty(name = "conductor.event-queues.nats.enabled", havingValue = "true") +public class NATSConfiguration { + + @Bean + public EventQueueProvider natsEventQueueProvider(Environment environment, Scheduler scheduler) { + return new NATSEventQueueProvider(environment, scheduler); + } +} diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java new file mode 100644 index 000000000..9b6d346fa --- /dev/null +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Netflix, Inc. + *

+ * 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 com.netflix.conductor.contribs.queue.nats.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.lang.NonNull; + +import com.netflix.conductor.contribs.queue.nats.NATSObservableQueue; +import com.netflix.conductor.core.events.EventQueueProvider; +import com.netflix.conductor.core.events.queue.ObservableQueue; + +import rx.Scheduler; + +/** + * @author Oleksiy Lysak + */ +public class NATSEventQueueProvider implements EventQueueProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(NATSEventQueueProvider.class); + + protected Map queues = new ConcurrentHashMap<>(); + private final Scheduler scheduler; + + public NATSEventQueueProvider(Environment environment, Scheduler scheduler) { + this.scheduler = scheduler; + LOGGER.info("NATS Event Queue Provider initialized..."); + } + + @Override + public String getQueueType() { + return "nats"; + } + + @Override + @NonNull + public ObservableQueue getQueue(String queueURI) { + NATSObservableQueue queue = + queues.computeIfAbsent(queueURI, q -> new NATSObservableQueue(queueURI, scheduler)); + if (queue.isClosed()) { + queue.open(); + } + return queue; + } +} diff --git a/settings.gradle b/settings.gradle index 8f3093f2f..60282ca25 100644 --- a/settings.gradle +++ b/settings.gradle @@ -71,6 +71,9 @@ include 'metrics' include 'es7-persistence' include 'azureblob-storage' include 'postgres-external-storage' +include 'amqp' +include 'nats' +include 'nats-streaming' include 'test-harness' From 57b1fa06fb09788f4907098fd5c3a835d89a4835 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 19:20:50 -0800 Subject: [PATCH 041/202] test fixes --- server/build.gradle | 39 ++++++++++++++++--- workflow-event-listener/build.gradle | 8 ++-- ...rchivingWithTTLWorkflowStatusListener.java | 5 ++- ...rchivingWorkflowListenerConfiguration.java | 1 + .../ArchivingWorkflowListenerProperties.java | 1 + .../ArchivingWorkflowStatusListener.java | 1 + .../ConductorQueueStatusPublisher.java | 1 + ...ctorQueueStatusPublisherConfiguration.java | 1 + ...nductorQueueStatusPublisherProperties.java | 1 + .../ArchivingWorkflowStatusListenerTest.java | 1 + ...orkflowStatusPublisherIntegrationTest.java | 2 + 11 files changed, 49 insertions(+), 12 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 38ebba808..aca956f76 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -16,19 +16,46 @@ plugins { } dependencies { - - implementation project(':conductor-rest') implementation project(':conductor-core') + implementation project(':conductor-rest') + implementation project(':conductor-grpc-server') + + //Event Systems + implementation project(':conductor-amqp') + implementation project(':conductor-nats') + implementation project(':conductor-nats-streaming') + implementation project(':conductor-awssqs-event-queue') + + //External Payload Storage + implementation project(':conductor-azureblob-storage') + implementation project(':conductor-postgres-external-storage') + implementation project(':conductor-awss3-storage') + + + //Persistence implementation project(':conductor-redis-persistence') implementation project(':conductor-cassandra-persistence') - implementation project(':conductor-es6-persistence') - implementation project(':conductor-grpc-server') + implementation project(':conductor-postgres-persistence') + implementation project(':conductor-mysql-persistence') + + //Indexing (note: Elasticsearch 6 is deprecated) + implementation project(':conductor-es7-persistence') + + implementation project(':conductor-redis-lock') implementation project(':conductor-redis-concurrency-limit') + + //System Tasks implementation project(':conductor-http-task') implementation project(':conductor-json-jq-task') - implementation project(':conductor-awss3-storage') - implementation project(':conductor-awssqs-event-queue') + implementation project(':conductor-kafka') + + //Metrics + implementation project(':conductor-metrics') + + //Event Listener + implementation project(':conductor-workflow-event-listener') + implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-validation' diff --git a/workflow-event-listener/build.gradle b/workflow-event-listener/build.gradle index 840bff300..b30e145ee 100644 --- a/workflow-event-listener/build.gradle +++ b/workflow-event-listener/build.gradle @@ -5,19 +5,19 @@ dependencies { implementation project(':conductor-common') implementation project(':conductor-core') + implementation project(':conductor-redis-persistence') compileOnly 'org.springframework.boot:spring-boot-starter' compileOnly 'org.springframework.boot:spring-boot-starter-web' + testImplementation project(':conductor-server') testImplementation "org.apache.groovy:groovy-all:${revGroovy}" testImplementation "org.spockframework:spock-core:${revSpock}" testImplementation "org.spockframework:spock-spring:${revSpock}" - implementation "org.springframework.boot:spring-boot-starter-log4j2" - - - implementation project(':conductor-server') + testImplementation 'org.springframework.retry:spring-retry' testImplementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation "com.netflix.dyno-queues:dyno-queues-redis:${revDynoQueues}" testImplementation project(':conductor-test-util').sourceSets.test.output //In memory diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java index 12ac5d268..cc58948c1 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -14,8 +15,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; - -import jakarta.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +23,8 @@ import com.netflix.conductor.metrics.Monitors; import com.netflix.conductor.model.WorkflowModel; +import jakarta.annotation.*; + public class ArchivingWithTTLWorkflowStatusListener implements WorkflowStatusListener { private static final Logger LOGGER = diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java index b6de249cb..50978417d 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java index f687516f3..53361429a 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java index 9016d33f4..c784c38a4 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java index 75b3166a3..d10bf5f73 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java index 83a32fe10..8cdf76f14 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java index 522279385..4073ce7ad 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java index 65372ac86..7532fb01d 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java index 86fc0017c..4f9f002b6 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -51,6 +52,7 @@ @RunWith(SpringRunner.class) @SpringBootTest( properties = { + "conductor.db.type=memory", "conductor.workflow-status-listener.type=queue_publisher", "conductor.workflow-status-listener.queue-publisher.successQueue=dummy", "conductor.workflow-status-listener.queue-publisher.failureQueue=dummy", From a9c1f1da7a6a0b06ec5474f87f133e104abafad4 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 19:59:09 -0800 Subject: [PATCH 042/202] datadog fixes --- dependencies.gradle | 2 +- metrics/build.gradle | 2 +- metrics/dependencies.lock | 376 +++++- server/dependencies.lock | 1008 +++++++++++++++-- .../java/com/netflix/conductor/Conductor.java | 4 + .../src/main/resources/application.properties | 13 +- 6 files changed, 1247 insertions(+), 158 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 8f0faeb6b..874690a80 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -59,7 +59,7 @@ ext { revFasterXml = '2.15.3' revAmqpClient = '5.13.0' revKafka = '2.6.0' - revMicrometer = '1.6.2' + revMicrometer = '1.8.12' revPrometheus = '0.9.0' revElasticSearch7 = '7.12.1' revCodec = '1.15' diff --git a/metrics/build.gradle b/metrics/build.gradle index 4fdf59601..25d0a8d92 100644 --- a/metrics/build.gradle +++ b/metrics/build.gradle @@ -19,7 +19,7 @@ dependencies { implementation "io.prometheus:simpleclient:${revPrometheus}" implementation "io.micrometer:micrometer-registry-prometheus:${revMicrometer}" - implementation 'io.micrometer:micrometer-registry-datadog:1.9.1' + implementation 'io.micrometer:micrometer-registry-datadog:1.12.1' testImplementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/metrics/dependencies.lock b/metrics/dependencies.lock index 2b3af75aa..79272c513 100644 --- a/metrics/dependencies.lock +++ b/metrics/dependencies.lock @@ -1,18 +1,18 @@ { "annotationProcessor": { "org.springframework.boot:spring-boot-configuration-processor": { - "locked": "2.7.16" + "locked": "3.1.4" } }, "compileClasspath": { "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true }, "com.netflix.spectator:spectator-reg-metrics3": { "locked": "0.122.0" @@ -21,10 +21,10 @@ "locked": "0.122.0" }, "io.micrometer:micrometer-registry-datadog": { - "locked": "1.9.1" + "locked": "1.12.1" }, "io.micrometer:micrometer-registry-prometheus": { - "locked": "1.6.2" + "locked": "1.8.12" }, "io.prometheus:simpleclient": { "locked": "0.9.0" @@ -39,36 +39,96 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.springframework.boot:spring-boot-starter": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "3.1.4" } }, "runtimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" + }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" }, "com.netflix.spectator:spectator-reg-metrics3": { "locked": "0.122.0" @@ -76,49 +136,124 @@ "com.netflix.spectator:spectator-reg-micrometer": { "locked": "0.122.0" }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.7" + }, "io.micrometer:micrometer-registry-datadog": { - "locked": "1.9.1" + "locked": "1.12.1" }, "io.micrometer:micrometer-registry-prometheus": { - "locked": "1.6.2" + "locked": "1.8.12" }, "io.prometheus:simpleclient": { "locked": "0.9.0" }, "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], "locked": "1.2.2" }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" + }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" } }, "testCompileClasspath": { "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true }, "com.netflix.spectator:spectator-reg-metrics3": { "locked": "0.122.0" @@ -127,10 +262,10 @@ "locked": "0.122.0" }, "io.micrometer:micrometer-registry-datadog": { - "locked": "1.9.1" + "locked": "1.12.1" }, "io.micrometer:micrometer-registry-prometheus": { - "locked": "1.6.2" + "locked": "1.8.12" }, "io.prometheus:simpleclient": { "locked": "0.9.0" @@ -148,48 +283,108 @@ "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.mock-server:mockserver-client-java": { "locked": "5.12.0" }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.testcontainers:mockserver": { - "locked": "1.18.3" + "locked": "1.15.3" } }, "testRuntimeClasspath": { + "com.fasterxml.jackson.core:jackson-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.core:jackson-databind": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.15.2" + }, + "com.fasterxml.jackson.module:jackson-module-afterburner": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.15.2" + }, + "com.github.ben-manes.caffeine:caffeine": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "3.1.8" + }, "com.google.guava:guava": { - "locked": "32.1.2-jre" + "locked": "30.0-jre" + }, + "com.google.protobuf:protobuf-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "3.21.12" + }, + "com.jayway.jsonpath:json-path": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.8.0" + }, + "com.netflix.conductor:conductor-annotations": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "project": true }, "com.netflix.conductor:conductor-common": { - "locked": "3.15.0" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "project": true }, "com.netflix.conductor:conductor-core": { - "locked": "3.15.0" + "project": true + }, + "com.netflix.spectator:spectator-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.122.0" }, "com.netflix.spectator:spectator-reg-metrics3": { "locked": "0.122.0" @@ -197,59 +392,134 @@ "com.netflix.spectator:spectator-reg-micrometer": { "locked": "0.122.0" }, + "com.spotify:completable-futures": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "0.3.3" + }, + "commons-io:commons-io": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.11.0" + }, "io.micrometer:micrometer-registry-datadog": { - "locked": "1.9.1" + "locked": "1.12.1" }, "io.micrometer:micrometer-registry-prometheus": { - "locked": "1.6.2" + "locked": "1.8.12" }, "io.prometheus:simpleclient": { "locked": "0.9.0" }, "io.reactivex:rxjava": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], "locked": "1.2.2" }, + "jakarta.activation:jakarta.activation-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "2.1.2" + }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "4.0.1" + }, "javax.ws.rs:jsr311-api": { "locked": "1.1.1" }, "junit:junit": { "locked": "4.13.2" }, + "org.apache.bval:bval-jsr": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.0.5" + }, "org.apache.commons:commons-lang3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], "locked": "3.12.0" }, "org.apache.logging.log4j:log4j-api": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { - "locked": "2.17.2" + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-annotations", + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-core" + ], + "locked": "2.20.0" }, "org.junit.vintage:junit-vintage-engine": { - "locked": "5.8.2" + "locked": "5.9.3" }, "org.mock-server:mockserver-client-java": { "locked": "5.12.0" }, + "org.openjdk.nashorn:nashorn-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-core" + ], + "locked": "15.4" + }, + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common" + ], + "locked": "2.1.0" + }, "org.springframework.boot:spring-boot-starter-log4j2": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-web": { - "locked": "2.7.16" + "locked": "3.1.4" }, "org.testcontainers:mockserver": { - "locked": "1.18.3" + "locked": "1.15.3" } } } \ No newline at end of file diff --git a/server/dependencies.lock b/server/dependencies.lock index 551cb5f20..8d28e79b1 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -5,19 +5,25 @@ } }, "compileClasspath": { + "com.netflix.conductor:conductor-amqp": { + "project": true + }, "com.netflix.conductor:conductor-awss3-storage": { "project": true }, "com.netflix.conductor:conductor-awssqs-event-queue": { "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true }, "com.netflix.conductor:conductor-core": { "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc-server": { @@ -29,6 +35,27 @@ "com.netflix.conductor:conductor-json-jq-task": { "project": true }, + "com.netflix.conductor:conductor-kafka": { + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "project": true }, @@ -41,6 +68,9 @@ "com.netflix.conductor:conductor-rest": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "project": true + }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.7" }, @@ -59,8 +89,8 @@ "org.apache.logging.log4j:log4j-web": { "locked": "2.20.0" }, - "org.springdoc:springdoc-openapi-ui": { - "locked": "1.6.15" + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -97,6 +127,12 @@ ], "locked": "1.11.86" }, + "com.azure:azure-storage-blob": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-azureblob-storage" + ], + "locked": "12.7.0" + }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-cassandra-persistence" @@ -112,14 +148,22 @@ "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -138,8 +182,15 @@ }, "com.google.guava:guava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "32.0.1-jre" }, @@ -157,6 +208,9 @@ ], "locked": "2.8.0" }, + "com.netflix.conductor:conductor-amqp": { + "project": true + }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -169,43 +223,76 @@ "com.netflix.conductor:conductor-awssqs-event-queue": { "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "project": true }, "com.netflix.conductor:conductor-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc": { @@ -223,6 +310,27 @@ "com.netflix.conductor:conductor-json-jq-task": { "project": true }, + "com.netflix.conductor:conductor-kafka": { + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "project": true }, @@ -230,11 +338,17 @@ "project": true }, "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "project": true }, "com.netflix.conductor:conductor-rest": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "project": true + }, "com.netflix.dyno-queues:dyno-queues-redis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -253,6 +367,24 @@ ], "locked": "0.122.0" }, + "com.netflix.spectator:spectator-reg-metrics3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp" + ], + "locked": "5.17.1" + }, "com.spotify:completable-futures": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" @@ -265,10 +397,16 @@ ], "locked": "1.4.20" }, + "commons-codec:commons-codec": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage" + ], + "locked": "1.15" + }, "commons-io:commons-io": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "2.7" }, @@ -297,13 +435,49 @@ ], "locked": "1.57.2" }, + "io.micrometer:micrometer-registry-datadog": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.micrometer:micrometer-registry-prometheus": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.nats:java-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.2.3" + }, + "io.nats:jnats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.15.6" + }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.7" }, + "io.prometheus:simpleclient": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.16.0" + }, "io.reactivex:rxjava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" ], "locked": "1.2.2" }, @@ -333,10 +507,18 @@ }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-http-task" + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics" ], "locked": "1.1.1" }, + "mysql:mysql-connector-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "8.0.33" + }, "net.thisptr:jackson-jq": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-json-jq-task" @@ -352,13 +534,22 @@ }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock" ], @@ -370,123 +561,191 @@ ], "locked": "5.2.1" }, + "org.apache.kafka:kafka-clients": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-kafka" + ], + "locked": "3.4.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" + ], + "locked": "7.12.1" + }, + "org.flywaydb:flyway-core": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" ], - "locked": "6.8.12" + "locked": "9.16.3" }, - "org.elasticsearch.client:transport": { + "org.flywaydb:flyway-mysql": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-mysql-persistence" ], - "locked": "6.8.12" + "locked": "9.16.3" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" @@ -497,9 +756,17 @@ ], "locked": "15.4" }, + "org.postgresql:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "42.6.0" + }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "0.0.17" }, @@ -509,11 +776,13 @@ ], "locked": "3.13.3" }, - "org.springdoc:springdoc-openapi-ui": { + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-postgres-external-storage", "com.netflix.conductor:conductor-rest" ], - "locked": "1.6.15" + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -521,7 +790,18 @@ "org.springframework.boot:spring-boot-starter-actuator": { "locked": "3.1.4" }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "3.1.4" + }, "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { @@ -557,6 +837,12 @@ ], "locked": "1.11.86" }, + "com.azure:azure-storage-blob": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-azureblob-storage" + ], + "locked": "12.7.0" + }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-cassandra-persistence" @@ -572,14 +858,22 @@ "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -598,8 +892,15 @@ }, "com.google.guava:guava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "32.0.1-jre" }, @@ -617,6 +918,9 @@ ], "locked": "2.8.0" }, + "com.netflix.conductor:conductor-amqp": { + "project": true + }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -629,43 +933,76 @@ "com.netflix.conductor:conductor-awssqs-event-queue": { "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "project": true }, "com.netflix.conductor:conductor-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc": { @@ -683,6 +1020,27 @@ "com.netflix.conductor:conductor-json-jq-task": { "project": true }, + "com.netflix.conductor:conductor-kafka": { + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "project": true }, @@ -690,11 +1048,17 @@ "project": true }, "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "project": true }, "com.netflix.conductor:conductor-rest": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "project": true + }, "com.netflix.dyno-queues:dyno-queues-redis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -713,6 +1077,24 @@ ], "locked": "0.122.0" }, + "com.netflix.spectator:spectator-reg-metrics3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp" + ], + "locked": "5.17.1" + }, "com.spotify:completable-futures": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" @@ -725,10 +1107,16 @@ ], "locked": "1.4.20" }, + "commons-codec:commons-codec": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage" + ], + "locked": "1.15" + }, "commons-io:commons-io": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "2.7" }, @@ -757,13 +1145,49 @@ ], "locked": "1.57.2" }, + "io.micrometer:micrometer-registry-datadog": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.micrometer:micrometer-registry-prometheus": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.nats:java-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.2.3" + }, + "io.nats:jnats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.15.6" + }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.7" }, + "io.prometheus:simpleclient": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.16.0" + }, "io.reactivex:rxjava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" ], "locked": "1.2.2" }, @@ -793,10 +1217,18 @@ }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-http-task" + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics" ], "locked": "1.1.1" }, + "mysql:mysql-connector-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "8.0.33" + }, "net.thisptr:jackson-jq": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-json-jq-task" @@ -812,13 +1244,22 @@ }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock" ], @@ -830,123 +1271,191 @@ ], "locked": "5.2.1" }, + "org.apache.kafka:kafka-clients": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-kafka" + ], + "locked": "3.4.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.12" + "locked": "7.12.1" }, - "org.elasticsearch.client:transport": { + "org.flywaydb:flyway-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" ], - "locked": "6.8.12" + "locked": "9.16.3" + }, + "org.flywaydb:flyway-mysql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "9.16.3" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" @@ -957,9 +1466,17 @@ ], "locked": "15.4" }, + "org.postgresql:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "42.6.0" + }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "0.0.17" }, @@ -969,11 +1486,13 @@ ], "locked": "3.13.3" }, - "org.springdoc:springdoc-openapi-ui": { + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-postgres-external-storage", "com.netflix.conductor:conductor-rest" ], - "locked": "1.6.15" + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -981,7 +1500,18 @@ "org.springframework.boot:spring-boot-starter-actuator": { "locked": "3.1.4" }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "3.1.4" + }, "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-validation": { @@ -1008,12 +1538,18 @@ "com.google.protobuf:protobuf-java": { "locked": "3.21.12" }, + "com.netflix.conductor:conductor-amqp": { + "project": true + }, "com.netflix.conductor:conductor-awss3-storage": { "project": true }, "com.netflix.conductor:conductor-awssqs-event-queue": { "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true }, @@ -1023,7 +1559,7 @@ "com.netflix.conductor:conductor-core": { "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc-server": { @@ -1035,6 +1571,27 @@ "com.netflix.conductor:conductor-json-jq-task": { "project": true }, + "com.netflix.conductor:conductor-kafka": { + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "project": true }, @@ -1047,6 +1604,9 @@ "com.netflix.conductor:conductor-rest": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "project": true + }, "io.grpc:grpc-protobuf": { "locked": "1.57.2" }, @@ -1080,8 +1640,8 @@ "org.junit.vintage:junit-vintage-engine": { "locked": "5.9.3" }, - "org.springdoc:springdoc-openapi-ui": { - "locked": "1.6.15" + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -1121,6 +1681,12 @@ ], "locked": "1.11.86" }, + "com.azure:azure-storage-blob": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-azureblob-storage" + ], + "locked": "12.7.0" + }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-cassandra-persistence" @@ -1136,14 +1702,22 @@ "com.fasterxml.jackson.core:jackson-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, "com.fasterxml.jackson.core:jackson-databind": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -1162,8 +1736,15 @@ }, "com.google.guava:guava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "32.0.1-jre" }, @@ -1181,6 +1762,9 @@ ], "locked": "2.8.0" }, + "com.netflix.conductor:conductor-amqp": { + "project": true + }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -1193,43 +1777,76 @@ "com.netflix.conductor:conductor-awssqs-event-queue": { "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "project": true }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" + ], + "project": true + }, + "com.netflix.conductor:conductor-common-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "project": true }, "com.netflix.conductor:conductor-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-common-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc": { @@ -1247,6 +1864,27 @@ "com.netflix.conductor:conductor-json-jq-task": { "project": true }, + "com.netflix.conductor:conductor-kafka": { + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "project": true }, @@ -1254,11 +1892,17 @@ "project": true }, "com.netflix.conductor:conductor-redis-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "project": true }, "com.netflix.conductor:conductor-rest": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "project": true + }, "com.netflix.dyno-queues:dyno-queues-redis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -1277,6 +1921,24 @@ ], "locked": "0.122.0" }, + "com.netflix.spectator:spectator-reg-metrics3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp" + ], + "locked": "5.17.1" + }, "com.spotify:completable-futures": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" @@ -1289,10 +1951,16 @@ ], "locked": "1.4.20" }, + "commons-codec:commons-codec": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage" + ], + "locked": "1.15" + }, "commons-io:commons-io": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "2.7" }, @@ -1324,13 +1992,49 @@ "io.grpc:grpc-testing": { "locked": "1.57.2" }, + "io.micrometer:micrometer-registry-datadog": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.micrometer:micrometer-registry-prometheus": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.nats:java-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.2.3" + }, + "io.nats:jnats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.15.6" + }, "io.orkes.queues:orkes-conductor-queues": { "locked": "1.0.7" }, + "io.prometheus:simpleclient": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.16.0" + }, "io.reactivex:rxjava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" ], "locked": "1.2.2" }, @@ -1360,13 +2064,21 @@ }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-http-task" + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics" ], "locked": "1.1.1" }, "junit:junit": { "locked": "4.13.2" }, + "mysql:mysql-connector-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "8.0.33" + }, "net.thisptr:jackson-jq": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-json-jq-task" @@ -1382,13 +2094,22 @@ }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock" ], @@ -1400,123 +2121,191 @@ ], "locked": "5.2.1" }, + "org.apache.kafka:kafka-clients": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-kafka" + ], + "locked": "3.4.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.12" + "locked": "7.12.1" }, - "org.elasticsearch.client:transport": { + "org.flywaydb:flyway-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" ], - "locked": "6.8.12" + "locked": "9.16.3" + }, + "org.flywaydb:flyway-mysql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "9.16.3" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" @@ -1530,9 +2319,17 @@ ], "locked": "15.4" }, + "org.postgresql:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "42.6.0" + }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "0.0.17" }, @@ -1542,11 +2339,13 @@ ], "locked": "3.13.3" }, - "org.springdoc:springdoc-openapi-ui": { + "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-postgres-external-storage", "com.netflix.conductor:conductor-rest" ], - "locked": "1.6.15" + "locked": "2.1.0" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -1554,7 +2353,18 @@ "org.springframework.boot:spring-boot-starter-actuator": { "locked": "3.1.4" }, + "org.springframework.boot:spring-boot-starter-jdbc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "3.1.4" + }, "org.springframework.boot:spring-boot-starter-log4j2": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-workflow-event-listener" + ], "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-test": { diff --git a/server/src/main/java/com/netflix/conductor/Conductor.java b/server/src/main/java/com/netflix/conductor/Conductor.java index be5bda17b..aa55b7804 100644 --- a/server/src/main/java/com/netflix/conductor/Conductor.java +++ b/server/src/main/java/com/netflix/conductor/Conductor.java @@ -50,7 +50,11 @@ public static void main(String[] args) throws IOException { */ private static void loadExternalConfig() throws IOException { String configFile = System.getProperty("CONDUCTOR_CONFIG_FILE"); + if (StringUtils.isBlank(configFile)) { + configFile = System.getenv("CONDUCTOR_CONFIG_FILE"); + } if (StringUtils.isNotBlank(configFile)) { + log.info("Loading {}", configFile); FileSystemResource resource = new FileSystemResource(configFile); if (resource.exists()) { Properties properties = new Properties(); diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 9889a2022..aed35bab9 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -57,7 +57,7 @@ conductor.elasticsearch.url=localhost:9300 conductor.elasticsearch.indexName=conductor #Elasticsearch major release version. -conductor.elasticsearch.version=6 +conductor.elasticsearch.version=7 #conductor.elasticsearch.version=7 # Default event queue type to listen on for wait task @@ -120,9 +120,14 @@ conductor.workflow-execution-lock.type=noop_lock # conductor.app.summary-input-output-json-serialization.enabled=true # Additional modules for metrics collection exposed to Prometheus (optional) -# conductor.metrics-prometheus.enabled=true + conductor.metrics-prometheus.enabled=true # management.endpoints.web.exposure.include=prometheus # Additional modules for metrics collection exposed to Datadog (optional) -management.metrics.export.datadog.enabled=${conductor.metrics-datadog.enabled:false} -management.metrics.export.datadog.api-key=${conductor.metrics-datadog.api-key:} +management.datadog.metrics.export.enabled=false +# management.datadog.metrics.export.apiKey=YOUR_API_KEY +# management.datadog.metrics.export.uri=dd instance URL +# management.datadog.metrics.export.step=10s + + + From 5587e26f950841f9a0a1010fba379bf1d6687cd0 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 20:03:28 -0800 Subject: [PATCH 043/202] Update application.properties --- server/src/main/resources/application.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index aed35bab9..9632b3e29 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -131,3 +131,4 @@ management.datadog.metrics.export.enabled=false +management.endpoints.web.exposure.include=prometheus From 72543e47ccfd805489cc5262cc6a54005719c56e Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Sun, 17 Dec 2023 20:04:10 -0800 Subject: [PATCH 044/202] formatting --- .../com/netflix/conductor/dao/ExecutionDAOTest.java | 1 + .../test/java/com/netflix/conductor/dao/TestBase.java | 1 + .../conductor/mysql/config/MySQLConfiguration.java | 1 + .../conductor/mysql/config/MySQLProperties.java | 1 + .../com/netflix/conductor/mysql/dao/MySQLBaseDAO.java | 1 + .../conductor/mysql/dao/MySQLExecutionDAO.java | 1 + .../netflix/conductor/mysql/dao/MySQLMetadataDAO.java | 1 + .../netflix/conductor/mysql/dao/MySQLQueueDAO.java | 1 + .../netflix/conductor/mysql/util/ExecuteFunction.java | 1 + .../netflix/conductor/mysql/util/LazyToString.java | 1 + .../java/com/netflix/conductor/mysql/util/Query.java | 1 + .../netflix/conductor/mysql/util/QueryFunction.java | 1 + .../conductor/mysql/util/ResultSetHandler.java | 1 + .../conductor/mysql/util/TransactionalFunction.java | 1 + .../conductor/mysql/dao/MySQLExecutionDAOTest.java | 1 + .../conductor/mysql/dao/MySQLMetadataDAOTest.java | 1 + .../conductor/mysql/dao/MySQLQueueDAOTest.java | 1 + .../integration/grpc/mysql/MySQLGrpcEndToEndTest.java | 1 + .../postgres/config/PostgresPayloadConfiguration.java | 4 +++- .../postgres/config/PostgresPayloadProperties.java | 1 + .../controller/ExternalPostgresPayloadResource.java | 1 + .../postgres/storage/PostgresPayloadStorage.java | 1 + .../ExternalPostgresPayloadResourceTest.java | 1 + .../postgres/storage/PostgresPayloadStorageTest.java | 1 + .../postgres/storage/PostgresPayloadTestUtil.java | 1 + .../postgres/config/PostgresConfiguration.java | 3 ++- .../conductor/postgres/config/PostgresProperties.java | 1 + .../conductor/postgres/dao/PostgresBaseDAO.java | 1 + .../conductor/postgres/dao/PostgresExecutionDAO.java | 3 ++- .../conductor/postgres/dao/PostgresIndexDAO.java | 1 + .../conductor/postgres/dao/PostgresMetadataDAO.java | 3 ++- .../conductor/postgres/dao/PostgresQueueDAO.java | 3 ++- .../conductor/postgres/util/ExecuteFunction.java | 1 + .../conductor/postgres/util/ExecutorsUtil.java | 1 + .../netflix/conductor/postgres/util/LazyToString.java | 1 + .../postgres/util/PostgresIndexQueryBuilder.java | 1 + .../com/netflix/conductor/postgres/util/Query.java | 1 + .../conductor/postgres/util/QueryFunction.java | 1 + .../conductor/postgres/util/ResultSetHandler.java | 1 + .../postgres/util/TransactionalFunction.java | 1 + .../postgres/dao/PostgresExecutionDAOTest.java | 1 + .../conductor/postgres/dao/PostgresIndexDAOTest.java | 1 + .../postgres/dao/PostgresMetadataDAOTest.java | 1 + .../conductor/postgres/dao/PostgresQueueDAOTest.java | 11 ++++++----- .../postgres/performance/PerformanceTest.java | 1 + .../postgres/util/PostgresIndexQueryBuilderTest.java | 1 + .../grpc/postgres/PostgresGrpcEndToEndTest.java | 1 + .../java/com/netflix/conductor/ConductorTestApp.java | 1 + .../common/config/TestObjectMapperConfiguration.java | 1 + .../test/integration/AbstractEndToEndTest.java | 1 + .../integration/grpc/AbstractGrpcEndToEndTest.java | 1 + 51 files changed, 62 insertions(+), 10 deletions(-) diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java index 32415e35c..a2688688d 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java index 24b3a52de..eb78c531b 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java index 17dd3cdb5..fe5cf9795 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java index 9ed9f1867..7c926114b 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java index 7e133c72f..2e6f76249 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java index 5bb94daf3..17731add1 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java index d04046db1..10d543860 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java index ffbaf506f..d84b0e310 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java index 44b51f2e9..668a8fa61 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java index 41f83ff35..da45247eb 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java index 1df27602c..aefa2024f 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java index 135f22425..5dde942cf 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java index 14c6d6108..a21c2ede4 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java index f82eb4d42..2e8978e7c 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java index 366205724..13908e342 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java index a357985bc..d61f972b7 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java index 9bf095320..09d1363b1 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java index a5210a88a..9e3e40eda 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java index c9e5819f5..fa8c68496 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -15,7 +16,6 @@ import javax.sql.DataSource; -import jakarta.annotation.*; import org.flywaydb.core.Flyway; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -29,6 +29,8 @@ import com.netflix.conductor.core.utils.IDGenerator; import com.netflix.conductor.postgres.storage.PostgresPayloadStorage; +import jakarta.annotation.*; + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(PostgresPayloadProperties.class) @ConditionalOnProperty(name = "conductor.external-payload-storage.type", havingValue = "postgres") diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java index f96ec98f5..007a00cce 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java index 1aeab3aae..1c485db90 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java index d71998ed9..0df04083a 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java index bea89e224..c39e703d5 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java index 976eb5780..31dbf11d5 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java index b31898ccc..c88cfb8cb 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java index e07c63243..f41e55a57 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -16,7 +17,6 @@ import javax.sql.DataSource; -import jakarta.annotation.*; import org.flywaydb.core.Flyway; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -35,6 +35,7 @@ import com.netflix.conductor.postgres.dao.PostgresQueueDAO; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.*; @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(PostgresProperties.class) diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java index 038faf987..1166bd4f8 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java index 694510638..57c3ccf77 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java index af9e5edaf..a2a358fa2 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -23,7 +24,6 @@ import javax.sql.DataSource; -import jakarta.annotation.*; import org.springframework.retry.support.RetryTemplate; import com.netflix.conductor.common.metadata.events.EventExecution; @@ -44,6 +44,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; +import jakarta.annotation.*; public class PostgresExecutionDAO extends PostgresBaseDAO implements ExecutionDAO, RateLimitingDAO, PollDataDAO, ConcurrentExecutionLimitDAO { diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java index 8895af230..d50407536 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java index c1a05dc6d..ab8c5acd1 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -20,7 +21,6 @@ import javax.sql.DataSource; -import jakarta.annotation.*; import org.springframework.retry.support.RetryTemplate; import com.netflix.conductor.common.metadata.events.EventHandler; @@ -36,6 +36,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Preconditions; +import jakarta.annotation.*; public class PostgresMetadataDAO extends PostgresBaseDAO implements MetadataDAO, EventHandlerDAO { diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java index 76275722b..206bd8569 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -20,7 +21,6 @@ import javax.sql.DataSource; -import jakarta.annotation.*; import org.springframework.retry.support.RetryTemplate; import com.netflix.conductor.core.events.queue.Message; @@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; +import jakarta.annotation.*; public class PostgresQueueDAO extends PostgresBaseDAO implements QueueDAO { diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java index 6b9593c7a..829bbded2 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java index 4a0ee2c2c..f0c3b3999 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java index 2d05d4267..2b2370950 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java index 256970fd9..74ce8c7ee 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java index 5d42a8b75..d65115c46 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java index 9d20097ca..d0f16e31a 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java index ec0b64827..838146ffe 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java index 06f28889f..c790947ed 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java index 795f8d04d..b1563b38e 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java index 0bb1d3235..73f62b68d 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java index a302b43c3..f86ae78bd 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java index caf291d9d..da22860e8 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 @@ -73,13 +74,13 @@ public class PostgresQueueDAOTest { @Autowired Flyway flyway; - // clean the database between tests. @Before public void before() { - try (Connection conn = dataSource.getConnection()){ + try (Connection conn = dataSource.getConnection()) { conn.setAutoCommit(true); - String[] stmts = new String[]{"truncate table queue;", "truncate table queue_message;"}; + String[] stmts = + new String[] {"truncate table queue;", "truncate table queue_message;"}; for (String stmt : stmts) { conn.prepareStatement(stmt).executeUpdate(); } @@ -334,7 +335,7 @@ public void pollDeferredMessagesTest() throws InterruptedException { } } - //@Test + // @Test public void processUnacksTest() { processUnacks( () -> { @@ -344,7 +345,7 @@ public void processUnacksTest() { "process_unacks_test"); } - //@Test + // @Test public void processAllUnacksTest() { processUnacks( () -> { diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java index 299444a95..1825fbb01 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java index 91041cd77..fdce7a3f2 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java index 53cc2333e..6697f2eb3 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java index 19d540f8c..8a86d2fa0 100644 --- a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java +++ b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java index 226ca0594..59f91c41b 100644 --- a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java +++ b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 61e1103b4..58512b352 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index b5cbcc2e1..df19deae7 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -1,4 +1,5 @@ /* + * Copyright 2023 Netflix, Inc. *

* 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 From 922f528180872caff666c9fad790fb2aed3d28e4 Mon Sep 17 00:00:00 2001 From: yanghaojia <2453883990@qq.com> Date: Mon, 18 Dec 2023 15:09:16 +0800 Subject: [PATCH 045/202] Fix elasticsearch dependency error --- dependencies.gradle | 2 +- es6-persistence/dependencies.lock | 24 +++++++++---------- .../es6/dao/index/ElasticSearchTest.java | 2 +- server/dependencies.lock | 18 +++++++------- test-harness/dependencies.lock | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 41c0f0317..3cb8aff90 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -24,7 +24,7 @@ ext { revCassandraUnit = '3.11.2.0' revCommonsIo = '2.7' revDynoQueues = '2.0.20' - revElasticSearch6 = '6.8.12' + revElasticSearch6 = '6.8.23' revEmbeddedRedis = '0.6' revEurekaClient = '1.10.10' revGroovy = '4.0.9' diff --git a/es6-persistence/dependencies.lock b/es6-persistence/dependencies.lock index 90c1e4bed..0aa67059d 100644 --- a/es6-persistence/dependencies.lock +++ b/es6-persistence/dependencies.lock @@ -36,13 +36,13 @@ "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.springframework.boot:spring-boot-starter": { "locked": "3.1.4" @@ -206,13 +206,13 @@ "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -259,13 +259,13 @@ "locked": "3.1.6" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.9.3" @@ -444,13 +444,13 @@ "locked": "3.1.6" }, "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { - "locked": "6.8.12" + "locked": "6.8.23" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.9.3" diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java index 5045c351b..2cbc2ff86 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java @@ -51,7 +51,7 @@ public ElasticSearchProperties elasticSearchProperties() { new ElasticsearchContainer( DockerImageName.parse( "docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag("6.8.17")) // this should match the client version + .withTag("6.8.23")) // this should match the client version // Resolve issue with es container not starting on m1/m2 macs .withEnv(Map.of("bootstrap.system_call_filter", "false")); diff --git a/server/dependencies.lock b/server/dependencies.lock index 551cb5f20..9c2d10ef9 100644 --- a/server/dependencies.lock +++ b/server/dependencies.lock @@ -474,19 +474,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "8.7.1" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" @@ -934,19 +934,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "8.7.1" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" @@ -1504,19 +1504,19 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "8.7.1" + "locked": "6.8.23" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.elasticsearch.client:transport": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.glassfish.jaxb:jaxb-runtime": { "locked": "4.0.1" diff --git a/test-harness/dependencies.lock b/test-harness/dependencies.lock index 3473f8f56..a866dff22 100644 --- a/test-harness/dependencies.lock +++ b/test-harness/dependencies.lock @@ -672,7 +672,7 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-es6-persistence" ], - "locked": "6.8.12" + "locked": "6.8.23" }, "org.glassfish.jaxb:jaxb-runtime": { "firstLevelTransitive": [ From 5ef4cfdffc25f7cd3704daf9efc939ea58ba941b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 18 Dec 2023 22:44:24 -0800 Subject: [PATCH 046/202] docker compose --- docker/docker-compose-postgres-es7.yaml | 88 +++++++++++++++++++ docker/docker-compose-postgres.yaml | 28 ------ docker/server/Dockerfile | 52 +++++------ docker/server/config/config-mysql.properties | 6 +- .../config/config-postgres-es7.properties | 23 +++++ .../server/config/config-postgres.properties | 16 ++-- docker/server/config/config-redis.properties | 5 +- docker/server/config/config.properties | 2 +- docker/ui/Dockerfile | 2 +- mysql-persistence/build.gradle | 3 - postgres-persistence/build.gradle | 2 - 11 files changed, 153 insertions(+), 74 deletions(-) create mode 100644 docker/docker-compose-postgres-es7.yaml create mode 100755 docker/server/config/config-postgres-es7.properties diff --git a/docker/docker-compose-postgres-es7.yaml b/docker/docker-compose-postgres-es7.yaml new file mode 100644 index 000000000..1dff23682 --- /dev/null +++ b/docker/docker-compose-postgres-es7.yaml @@ -0,0 +1,88 @@ +version: '2.3' + +services: + conductor-server: + environment: + - CONFIG_PROP=config-postgres-es7.properties + image: conductor:server + container_name: conductor-server + build: + context: ../ + dockerfile: docker/server/Dockerfile + networks: + - internal + ports: + - 8080:8080 + - 5000:5000 + healthcheck: + test: [ "CMD", "curl","-I" ,"-XGET", "http://localhost:8080/health" ] + interval: 60s + timeout: 30s + retries: 12 + links: + - conductor-postgres:postgresdb + - conductor-elasticsearch:es + depends_on: + conductor-postgres: + condition: service_healthy + conductor-elasticsearch: + condition: service_healthy + logging: + driver: "json-file" + options: + max-size: "1k" + max-file: "3" + + conductor-postgres: + image: postgres + environment: + - POSTGRES_USER=conductor + - POSTGRES_PASSWORD=conductor + volumes: + - pgdata-conductor:/var/lib/postgresql/data + networks: + - internal + ports: + - 6432:5432 + healthcheck: + test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432' + interval: 5s + timeout: 5s + retries: 12 + logging: + driver: "json-file" + options: + max-size: "1k" + max-file: "3" + + conductor-elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.11 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx1024m" + - xpack.security.enabled=false + - discovery.type=single-node + volumes: + - esdata-conductor:/usr/share/elasticsearch/data + networks: + - internal + ports: + - 9201:9200 + healthcheck: + test: curl http://localhost:9200/_cluster/health -o /dev/null + interval: 5s + timeout: 5s + retries: 12 + logging: + driver: "json-file" + options: + max-size: "1k" + max-file: "3" + +volumes: + pgdata-conductor: + driver: local + esdata-conductor: + driver: local + +networks: + internal: diff --git a/docker/docker-compose-postgres.yaml b/docker/docker-compose-postgres.yaml index 12a6b7046..1e86fc69a 100644 --- a/docker/docker-compose-postgres.yaml +++ b/docker/docker-compose-postgres.yaml @@ -20,11 +20,8 @@ services: timeout: 30s retries: 12 links: - - conductor-elasticsearch:es - conductor-postgres:postgresdb depends_on: - conductor-elasticsearch: - condition: service_healthy conductor-postgres: condition: service_healthy logging: @@ -55,34 +52,9 @@ services: max-size: "1k" max-file: "3" - conductor-elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.11 - environment: - - "ES_JAVA_OPTS=-Xms512m -Xmx1024m" - - xpack.security.enabled=false - - discovery.type=single-node - volumes: - - esdata-conductor:/usr/share/elasticsearch/data - networks: - - internal - ports: - - 9201:9200 - healthcheck: - test: curl http://localhost:9200/_cluster/health -o /dev/null - interval: 5s - timeout: 5s - retries: 12 - logging: - driver: "json-file" - options: - max-size: "1k" - max-file: "3" - volumes: pgdata-conductor: driver: local - esdata-conductor: - driver: local networks: internal: diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 9b44f47a9..c7ffe1c0e 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,52 +1,48 @@ # -# conductor:server - Combined conductor server & UI +# conductor:server - Build Conductor Server # # =========================================================================================================== -# 0. Builder stage +# 0. Builder stage 1 # =========================================================================================================== -FROM alpine:3.18 AS builder +FROM openjdk:17-bullseye AS builder LABEL maintainer="Orkes OSS " +# Copy the project directly onto the image +COPY . /conductor +WORKDIR /conductor + +# Build the server on run +RUN ./gradlew build -x test +WORKDIR /server/build/libs +RUN ls -ltr + + +# +# conductor:server - Build Conductor UI +# # =========================================================================================================== -# 0. Build Conductor Server +# 1. Builder stage 2 # =========================================================================================================== +FROM alpine:3.18 AS ui-builder +LABEL maintainer="Orkes OSS " # Install dependencies -RUN apk add openjdk17 -RUN apk add git RUN apk add --update nodejs npm yarn COPY . /conductor WORKDIR /conductor/ui + # Include monaco sources into bundle (instead of using CDN) ENV REACT_APP_MONACO_EDITOR_USING_CDN=false RUN yarn install && cp -r node_modules/monaco-editor public/ && yarn build RUN ls -ltr RUN echo "Done building UI" -# Checkout the community project -WORKDIR / -RUN mkdir server-build -WORKDIR server-build -RUN ls -ltr - -RUN git clone https://github.com/conductor-oss/conductor-community.git - -# Copy the project directly onto the image -WORKDIR conductor-community -RUN ls -ltr - -# Build the server on run -RUN ./gradlew build -x test --stacktrace -WORKDIR /server-build -RUN ls -ltr -RUN pwd - # =========================================================================================================== -# 1. Bin stage +# 2. Bin stage # =========================================================================================================== FROM alpine:3.18 @@ -61,13 +57,13 @@ RUN mkdir -p /app/config /app/logs /app/libs # Copy the compiled output to new image COPY docker/server/bin /app COPY docker/server/config /app/config -COPY --from=builder /server-build/conductor-community/community-server/build/libs/*boot*.jar /app/libs/conductor-server.jar +COPY --from=builder /conductor/server/build/libs/*boot*.jar /app/libs/conductor-server.jar # Copy compiled UI assets to nginx www directory WORKDIR /usr/share/nginx/html RUN rm -rf ./* -COPY --from=builder /conductor/ui/build . -COPY --from=builder /conductor/docker/server/nginx/nginx.conf /etc/nginx/http.d/default.conf +COPY --from=ui-builder /conductor/ui/build . +COPY --from=ui-builder /conductor/docker/server/nginx/nginx.conf /etc/nginx/http.d/default.conf # Copy the files for the server into the app folders RUN chmod +x /app/startup.sh diff --git a/docker/server/config/config-mysql.properties b/docker/server/config/config-mysql.properties index 6b618dc2e..34526588f 100755 --- a/docker/server/config/config-mysql.properties +++ b/docker/server/config/config-mysql.properties @@ -1,13 +1,17 @@ # Database persistence type. conductor.db.type=mysql + # mysql spring.datasource.url=jdbc:mysql://mysql:3306/conductor spring.datasource.username=conductor spring.datasource.password=conductor -# Use redis queues +# redis queues conductor.queue.type=redis_standalone +conductor.redis.hosts=rs:6379:us-east-1c +conductor.redis-lock.serverAddress=redis://rs:6379 + # Elastic search instance indexing is enabled. conductor.indexing.enabled=true diff --git a/docker/server/config/config-postgres-es7.properties b/docker/server/config/config-postgres-es7.properties new file mode 100755 index 000000000..0bc4f6320 --- /dev/null +++ b/docker/server/config/config-postgres-es7.properties @@ -0,0 +1,23 @@ +# Database persistence type. +conductor.db.type=postgres +conductor.queue.type=postgres +conductor.external-payload-storage.type=postgres + +# postgres +spring.datasource.url=jdbc:postgresql://postgresdb:5432/postgres +spring.datasource.username=conductor +spring.datasource.password=conductor + +# Elastic search instance indexing is enabled. +conductor.indexing.enabled=true +conductor.elasticsearch.url=http://es:9200 +conductor.elasticsearch.indexName=conductor +conductor.elasticsearch.version=7 +conductor.elasticsearch.clusterHealthColor=yellow + +# Additional modules for metrics collection exposed to Prometheus (optional) +conductor.metrics-prometheus.enabled=true +management.endpoints.web.exposure.include=prometheus + +# Load sample kitchen-sink workflow +loadSample=true diff --git a/docker/server/config/config-postgres.properties b/docker/server/config/config-postgres.properties index 43aa64a0f..04b51a58c 100755 --- a/docker/server/config/config-postgres.properties +++ b/docker/server/config/config-postgres.properties @@ -1,21 +1,23 @@ # Database persistence type. conductor.db.type=postgres +conductor.queue.type=postgres +conductor.external-payload-storage.type=postgres -# postgres +# Database connectivity spring.datasource.url=jdbc:postgresql://postgresdb:5432/postgres spring.datasource.username=conductor spring.datasource.password=conductor -# Elastic search instance indexing is enabled. + +# Indexing Properties conductor.indexing.enabled=true -conductor.elasticsearch.url=http://es:9200 -conductor.elasticsearch.indexName=conductor -conductor.elasticsearch.version=7 -conductor.elasticsearch.clusterHealthColor=yellow +conductor.indexing.type=postgres +# Required to disable connecting to elasticsearch. +conductor.elasticsearch.version=0 # Additional modules for metrics collection exposed to Prometheus (optional) conductor.metrics-prometheus.enabled=true management.endpoints.web.exposure.include=prometheus # Load sample kitchen-sink workflow -loadSample=true +loadSample=true \ No newline at end of file diff --git a/docker/server/config/config-redis.properties b/docker/server/config/config-redis.properties index 5afd33797..b43e4b3c1 100755 --- a/docker/server/config/config-redis.properties +++ b/docker/server/config/config-redis.properties @@ -1,15 +1,14 @@ # Database persistence type. # Below are the properties for redis conductor.db.type=redis_standalone +conductor.queue.type=redis_standalone + conductor.redis.hosts=rs:6379:us-east-1c conductor.redis-lock.serverAddress=redis://rs:6379 conductor.redis.taskDefCacheRefreshInterval=1 conductor.redis.workflowNamespacePrefix=conductor conductor.redis.queueNamespacePrefix=conductor_queues -#Use redis queues -conductor.queue.type=redis_standalone - # Elastic search instance indexing is enabled. conductor.indexing.enabled=true conductor.elasticsearch.url=http://es:9200 diff --git a/docker/server/config/config.properties b/docker/server/config/config.properties index 74d414520..a85b02433 100755 --- a/docker/server/config/config.properties +++ b/docker/server/config/config.properties @@ -2,7 +2,7 @@ # db.type determines the type of database used # See various configurations below for the values -conductor.db.type=SET_THIS +#conductor.db.type=SET_THIS # =====================================================# # Redis Configuration Properties diff --git a/docker/ui/Dockerfile b/docker/ui/Dockerfile index 5762f7d1f..076774b6c 100644 --- a/docker/ui/Dockerfile +++ b/docker/ui/Dockerfile @@ -2,7 +2,7 @@ # conductor:ui - Netflix Conductor UI # FROM node:20-alpine -LABEL maintainer="Netflix OSS " +LABEL maintainer="Orkes OSS " # Install the required packages for the node build # to run on alpine diff --git a/mysql-persistence/build.gradle b/mysql-persistence/build.gradle index 3b060c6bc..80f3c24c4 100644 --- a/mysql-persistence/build.gradle +++ b/mysql-persistence/build.gradle @@ -20,9 +20,6 @@ dependencies { testImplementation "org.apache.groovy:groovy-all:${revGroovy}" - - testImplementation "org.elasticsearch:elasticsearch:6.8.23" - testImplementation "org.elasticsearch.client:transport:6.8.23" testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" diff --git a/postgres-persistence/build.gradle b/postgres-persistence/build.gradle index d0fef0332..15ae8a3c8 100644 --- a/postgres-persistence/build.gradle +++ b/postgres-persistence/build.gradle @@ -19,8 +19,6 @@ dependencies { testImplementation "org.apache.groovy:groovy-all:${revGroovy}" -// testImplementation "org.elasticsearch:elasticsearch:6.8.23" -// testImplementation "org.elasticsearch.client:transport:6.8.23" testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" From a08d1eb3ef3d7491ca8a3fa265a5a53912635b3a Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 18 Dec 2023 23:17:53 -0800 Subject: [PATCH 047/202] build fixes --- es6-persistence/build.gradle | 8 + mysql-persistence/build.gradle | 8 +- mysql-persistence/dependencies.lock | 338 +++++++++++++++--- .../grpc/mysql/MySQLGrpcEndToEndTest.java | 3 +- .../src/test/resources/application.properties | 2 +- .../integration/AbstractEndToEndTest.java | 2 +- 6 files changed, 304 insertions(+), 57 deletions(-) diff --git a/es6-persistence/build.gradle b/es6-persistence/build.gradle index d033895bb..9465340f6 100644 --- a/es6-persistence/build.gradle +++ b/es6-persistence/build.gradle @@ -11,6 +11,8 @@ * specific language governing permissions and limitations under the License. */ + + dependencies { implementation project(':conductor-common') implementation project(':conductor-core') @@ -32,3 +34,9 @@ dependencies { testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" testImplementation project(':conductor-common').sourceSets.test.output } + +switch (org.gradle.internal.os.OperatingSystem.current()) { + case org.gradle.internal.os.OperatingSystem.MAC_OS: + tasks.forEach(task -> task.onlyIf { project.hasProperty('ES6Test') }) + break; +} \ No newline at end of file diff --git a/mysql-persistence/build.gradle b/mysql-persistence/build.gradle index 80f3c24c4..dccdfc32f 100644 --- a/mysql-persistence/build.gradle +++ b/mysql-persistence/build.gradle @@ -20,16 +20,16 @@ dependencies { testImplementation "org.apache.groovy:groovy-all:${revGroovy}" - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" - testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" - testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" +// testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" +// testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" + testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" testImplementation "org.testcontainers:mysql:${revTestContainer}" testImplementation project(':conductor-server') testImplementation project(':conductor-client') testImplementation project(':conductor-grpc-client') - testImplementation project(':conductor-es6-persistence') + testImplementation project(':conductor-es7-persistence') testImplementation project(':conductor-test-util').sourceSets.test.output testImplementation project(':conductor-common-persistence').sourceSets.test.output diff --git a/mysql-persistence/dependencies.lock b/mysql-persistence/dependencies.lock index 2e635692f..9a4fe189f 100644 --- a/mysql-persistence/dependencies.lock +++ b/mysql-persistence/dependencies.lock @@ -270,7 +270,7 @@ "com.netflix.conductor:conductor-core": { "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc-client": { @@ -306,18 +306,6 @@ "org.apache.logging.log4j:log4j-web": { "locked": "2.20.0" }, - "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.23" - }, - "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.23" - }, - "org.elasticsearch.client:transport": { - "locked": "6.8.23" - }, - "org.elasticsearch:elasticsearch": { - "locked": "6.8.23" - }, "org.flywaydb:flyway-mysql": { "locked": "9.16.3" }, @@ -359,6 +347,12 @@ ], "locked": "1.11.86" }, + "com.azure:azure-storage-blob": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-azureblob-storage" + ], + "locked": "12.7.0" + }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-cassandra-persistence" @@ -375,7 +369,10 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -383,7 +380,10 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -414,9 +414,16 @@ }, "com.google.guava:guava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-es6-persistence", - "com.netflix.conductor:conductor-grpc-client" + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "30.0-jre" }, @@ -435,6 +442,12 @@ ], "locked": "2.8.0" }, + "com.netflix.conductor:conductor-amqp": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -453,6 +466,12 @@ ], "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" @@ -464,46 +483,71 @@ }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, "com.netflix.conductor:conductor-common-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" + ], "project": true }, "com.netflix.conductor:conductor-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], @@ -537,6 +581,48 @@ ], "project": true }, + "com.netflix.conductor:conductor-kafka": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" @@ -551,7 +637,8 @@ }, "com.netflix.conductor:conductor-redis-persistence": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, @@ -564,6 +651,12 @@ "com.netflix.conductor:conductor-server": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.dyno-queues:dyno-queues-redis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -589,6 +682,24 @@ ], "locked": "0.122.0" }, + "com.netflix.spectator:spectator-reg-metrics3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp" + ], + "locked": "5.17.1" + }, "com.spotify:completable-futures": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" @@ -607,11 +718,17 @@ ], "locked": "1.4.20" }, + "commons-codec:commons-codec": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage" + ], + "locked": "1.15" + }, "commons-io:commons-io": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "2.7" }, @@ -643,16 +760,52 @@ ], "locked": "1.57.2" }, + "io.micrometer:micrometer-registry-datadog": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.micrometer:micrometer-registry-prometheus": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.nats:java-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.2.3" + }, + "io.nats:jnats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.15.6" + }, "io.orkes.queues:orkes-conductor-queues": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], "locked": "1.0.7" }, + "io.prometheus:simpleclient": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.16.0" + }, "io.reactivex:rxjava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" ], "locked": "1.2.2" }, @@ -689,7 +842,9 @@ }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-http-task" + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics" ], "locked": "1.1.1" }, @@ -697,6 +852,9 @@ "locked": "4.13.2" }, "mysql:mysql-connector-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], "locked": "8.0.33" }, "net.thisptr:jackson-jq": { @@ -714,16 +872,24 @@ }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock" ], @@ -738,148 +904,205 @@ ], "locked": "5.2.1" }, + "org.apache.kafka:kafka-clients": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-kafka" + ], + "locked": "3.4.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.23" + "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.23" + "locked": "7.12.1" }, - "org.elasticsearch.client:transport": { + "org.flywaydb:flyway-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" ], - "locked": "6.8.23" - }, - "org.elasticsearch:elasticsearch": { - "locked": "6.8.23" + "locked": "9.16.3" }, "org.flywaydb:flyway-mysql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], "locked": "9.16.3" }, "org.glassfish.jaxb:jaxb-runtime": { @@ -903,9 +1126,17 @@ ], "locked": "15.4" }, + "org.postgresql:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], + "locked": "42.6.0" + }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "0.0.17" }, @@ -925,6 +1156,7 @@ "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-postgres-external-storage", "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], @@ -943,11 +1175,17 @@ "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-jdbc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "3.1.4" }, @@ -985,7 +1223,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-server" ], - "locked": "3.3.0" + "locked": "4.3.2" } } } \ No newline at end of file diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java index 9e3e40eda..d1e4715c6 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java @@ -34,7 +34,8 @@ "spring.datasource.username=root", "spring.datasource.password=root", "spring.datasource.hikari.maximum-pool-size=8", - "spring.datasource.hikari.minimum-idle=300000" + "spring.datasource.hikari.minimum-idle=300000", + "conductor.elasticsearch.version=7" }) public class MySQLGrpcEndToEndTest extends AbstractGrpcEndToEndTest { diff --git a/mysql-persistence/src/test/resources/application.properties b/mysql-persistence/src/test/resources/application.properties index d9e7b87cb..c64ffb657 100644 --- a/mysql-persistence/src/test/resources/application.properties +++ b/mysql-persistence/src/test/resources/application.properties @@ -1,5 +1,5 @@ conductor.db.type=mysql -spring.datasource.url=jdbc:tc:mysql:8.0.27:///conductor +spring.datasource.url=jdbc:tc:mysql:8.0.29:///conductor spring.datasource.username=root spring.datasource.password=root spring.datasource.hikari.maximum-pool-size=8 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 58512b352..00d3cab26 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -62,7 +62,7 @@ public abstract class AbstractEndToEndTest { private static final ElasticsearchContainer container = new ElasticsearchContainer( DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag("6.8.12")); // this should match the client version + .withTag("7.10.2")); // this should match the client version private static RestClient restClient; From 39faefd5e7450501c197994f1b9f2660645a7012 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 18 Dec 2023 23:42:16 -0800 Subject: [PATCH 048/202] build fixes --- postgres-persistence/build.gradle | 7 +- postgres-persistence/dependencies.lock | 348 +++++++++++++++--- .../postgres/PostgresGrpcEndToEndTest.java | 2 +- .../integration/AbstractEndToEndTest.java | 2 +- .../grpc/AbstractGrpcEndToEndTest.java | 6 +- 5 files changed, 299 insertions(+), 66 deletions(-) diff --git a/postgres-persistence/build.gradle b/postgres-persistence/build.gradle index 15ae8a3c8..66155a5d6 100644 --- a/postgres-persistence/build.gradle +++ b/postgres-persistence/build.gradle @@ -19,16 +19,13 @@ dependencies { testImplementation "org.apache.groovy:groovy-all:${revGroovy}" - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:6.8.23" - testImplementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.23" - testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" - testImplementation project(':conductor-server') testImplementation project(':conductor-client') testImplementation project(':conductor-grpc-client') - testImplementation project(':conductor-es6-persistence') + testImplementation project(':conductor-es7-persistence') testImplementation "org.testcontainers:postgresql:${revTestContainer}" + testImplementation "org.testcontainers:elasticsearch:${revTestContainer}" testImplementation project(':conductor-test-util').sourceSets.test.output testImplementation project(':conductor-common-persistence').sourceSets.test.output diff --git a/postgres-persistence/dependencies.lock b/postgres-persistence/dependencies.lock index 39d58d5e9..d3f1c6c7c 100644 --- a/postgres-persistence/dependencies.lock +++ b/postgres-persistence/dependencies.lock @@ -42,7 +42,7 @@ "locked": "2.20.0" }, "org.flywaydb:flyway-core": { - "locked": "9.21.0" + "locked": "9.16.3" }, "org.postgresql:postgresql": { "locked": "42.3.8" @@ -227,7 +227,7 @@ "locked": "2.20.0" }, "org.flywaydb:flyway-core": { - "locked": "9.21.0" + "locked": "9.16.3" }, "org.openjdk.nashorn:nashorn-core": { "firstLevelTransitive": [ @@ -270,7 +270,7 @@ "com.netflix.conductor:conductor-core": { "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "project": true }, "com.netflix.conductor:conductor-grpc-client": { @@ -303,20 +303,8 @@ "org.apache.logging.log4j:log4j-web": { "locked": "2.20.0" }, - "org.elasticsearch.client:elasticsearch-rest-client": { - "locked": "6.8.23" - }, - "org.elasticsearch.client:elasticsearch-rest-high-level-client": { - "locked": "6.8.23" - }, - "org.elasticsearch.client:transport": { - "locked": "6.8.23" - }, - "org.elasticsearch:elasticsearch": { - "locked": "6.8.23" - }, "org.flywaydb:flyway-core": { - "locked": "9.21.0" + "locked": "9.16.3" }, "org.junit.vintage:junit-vintage-engine": { "locked": "5.9.3" @@ -359,6 +347,12 @@ ], "locked": "1.11.86" }, + "com.azure:azure-storage-blob": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-azureblob-storage" + ], + "locked": "12.7.0" + }, "com.datastax.cassandra:cassandra-driver-core": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-cassandra-persistence" @@ -375,7 +369,10 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -383,7 +380,10 @@ "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "2.15.2" }, @@ -414,9 +414,16 @@ }, "com.google.guava:guava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-es6-persistence", - "com.netflix.conductor:conductor-grpc-client" + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-grpc-client", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence" ], "locked": "30.0-jre" }, @@ -435,6 +442,12 @@ ], "locked": "2.8.0" }, + "com.netflix.conductor:conductor-amqp": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-annotations": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common" @@ -453,6 +466,12 @@ ], "project": true }, + "com.netflix.conductor:conductor-azureblob-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-cassandra-persistence": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" @@ -464,46 +483,71 @@ }, "com.netflix.conductor:conductor-common": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-persistence", - "com.netflix.conductor:conductor-rest" + "com.netflix.conductor:conductor-rest", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, "com.netflix.conductor:conductor-common-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-es7-persistence", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-persistence" + ], "project": true }, "com.netflix.conductor:conductor-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-common-persistence", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, - "com.netflix.conductor:conductor-es6-persistence": { + "com.netflix.conductor:conductor-es7-persistence": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], @@ -537,6 +581,48 @@ ], "project": true }, + "com.netflix.conductor:conductor-kafka": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-metrics": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-mysql-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-nats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-postgres-external-storage": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, + "com.netflix.conductor:conductor-postgres-persistence": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.conductor:conductor-redis-concurrency-limit": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" @@ -551,7 +637,8 @@ }, "com.netflix.conductor:conductor-redis-persistence": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "project": true }, @@ -564,6 +651,12 @@ "com.netflix.conductor:conductor-server": { "project": true }, + "com.netflix.conductor:conductor-workflow-event-listener": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-server" + ], + "project": true + }, "com.netflix.dyno-queues:dyno-queues-redis": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-redis-persistence" @@ -589,6 +682,24 @@ ], "locked": "0.122.0" }, + "com.netflix.spectator:spectator-reg-metrics3": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.netflix.spectator:spectator-reg-micrometer": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.122.0" + }, + "com.rabbitmq:amqp-client": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp" + ], + "locked": "5.17.1" + }, "com.spotify:completable-futures": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-core" @@ -607,11 +718,17 @@ ], "locked": "1.4.20" }, + "commons-codec:commons-codec": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage" + ], + "locked": "1.15" + }, "commons-io:commons-io": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], "locked": "2.7" }, @@ -643,16 +760,52 @@ ], "locked": "1.57.2" }, + "io.micrometer:micrometer-registry-datadog": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.micrometer:micrometer-registry-prometheus": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "1.11.4" + }, + "io.nats:java-nats-streaming": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.2.3" + }, + "io.nats:jnats": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" + ], + "locked": "2.15.6" + }, "io.orkes.queues:orkes-conductor-queues": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-server" ], "locked": "1.0.7" }, + "io.prometheus:simpleclient": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-metrics" + ], + "locked": "0.16.0" + }, "io.reactivex:rxjava": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awssqs-event-queue", - "com.netflix.conductor:conductor-core" + "com.netflix.conductor:conductor-core", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming" ], "locked": "1.2.2" }, @@ -689,13 +842,21 @@ }, "javax.ws.rs:jsr311-api": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-http-task" + "com.netflix.conductor:conductor-http-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics" ], "locked": "1.1.1" }, "junit:junit": { "locked": "4.13.2" }, + "mysql:mysql-connector-java": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "8.0.33" + }, "net.thisptr:jackson-jq": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-json-jq-task" @@ -711,16 +872,24 @@ }, "org.apache.commons:commons-lang3": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock" ], @@ -735,149 +904,206 @@ ], "locked": "5.2.1" }, + "org.apache.kafka:kafka-clients": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-kafka" + ], + "locked": "3.4.1" + }, "org.apache.logging.log4j:log4j-api": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-core": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-jul": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-slf4j-impl": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.apache.logging.log4j:log4j-web": { "firstLevelTransitive": [ + "com.netflix.conductor:conductor-amqp", "com.netflix.conductor:conductor-annotations", "com.netflix.conductor:conductor-awss3-storage", "com.netflix.conductor:conductor-awssqs-event-queue", + "com.netflix.conductor:conductor-azureblob-storage", "com.netflix.conductor:conductor-cassandra-persistence", "com.netflix.conductor:conductor-client", "com.netflix.conductor:conductor-common", "com.netflix.conductor:conductor-common-persistence", "com.netflix.conductor:conductor-core", - "com.netflix.conductor:conductor-es6-persistence", + "com.netflix.conductor:conductor-es7-persistence", "com.netflix.conductor:conductor-grpc", "com.netflix.conductor:conductor-grpc-client", "com.netflix.conductor:conductor-grpc-server", "com.netflix.conductor:conductor-http-task", "com.netflix.conductor:conductor-json-jq-task", + "com.netflix.conductor:conductor-kafka", + "com.netflix.conductor:conductor-metrics", + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-nats", + "com.netflix.conductor:conductor-nats-streaming", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence", "com.netflix.conductor:conductor-redis-concurrency-limit", "com.netflix.conductor:conductor-redis-lock", "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-rest", - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "2.20.0" }, "org.elasticsearch.client:elasticsearch-rest-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.23" + "locked": "8.7.1" }, "org.elasticsearch.client:elasticsearch-rest-high-level-client": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-es7-persistence" ], - "locked": "6.8.23" + "locked": "7.12.1" }, - "org.elasticsearch.client:transport": { + "org.flywaydb:flyway-core": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-es6-persistence" + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" ], - "locked": "6.8.23" + "locked": "9.16.3" }, - "org.elasticsearch:elasticsearch": { - "locked": "6.8.23" - }, - "org.flywaydb:flyway-core": { - "locked": "9.21.0" + "org.flywaydb:flyway-mysql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence" + ], + "locked": "9.16.3" }, "org.glassfish.jaxb:jaxb-runtime": { "firstLevelTransitive": [ @@ -901,11 +1127,16 @@ "locked": "15.4" }, "org.postgresql:postgresql": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], "locked": "42.3.8" }, "org.rarefiedredis.redis:redis-java": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-redis-persistence" + "com.netflix.conductor:conductor-redis-persistence", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "0.0.17" }, @@ -925,6 +1156,7 @@ "org.springdoc:springdoc-openapi-starter-webmvc-ui": { "firstLevelTransitive": [ "com.netflix.conductor:conductor-common", + "com.netflix.conductor:conductor-postgres-external-storage", "com.netflix.conductor:conductor-rest", "com.netflix.conductor:conductor-server" ], @@ -943,11 +1175,17 @@ "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-jdbc": { + "firstLevelTransitive": [ + "com.netflix.conductor:conductor-mysql-persistence", + "com.netflix.conductor:conductor-postgres-external-storage", + "com.netflix.conductor:conductor-postgres-persistence" + ], "locked": "3.1.4" }, "org.springframework.boot:spring-boot-starter-log4j2": { "firstLevelTransitive": [ - "com.netflix.conductor:conductor-server" + "com.netflix.conductor:conductor-server", + "com.netflix.conductor:conductor-workflow-event-listener" ], "locked": "3.1.4" }, @@ -985,7 +1223,7 @@ "com.netflix.conductor:conductor-redis-persistence", "com.netflix.conductor:conductor-server" ], - "locked": "3.3.0" + "locked": "4.3.2" } } } \ No newline at end of file diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java index 6697f2eb3..98320e851 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java @@ -28,7 +28,7 @@ properties = { "conductor.db.type=postgres", "conductor.app.asyncIndexingEnabled=false", - "conductor.elasticsearch.version=6", + "conductor.elasticsearch.version=7", "conductor.grpc-server.port=8098", "conductor.indexing.type=elasticsearch", "spring.datasource.url=jdbc:tc:postgresql:11.15-alpine:///conductor", // "tc" prefix diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 00d3cab26..59702412b 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -48,7 +48,7 @@ import static org.junit.Assert.assertNotNull; @TestPropertySource( - properties = {"conductor.indexing.enabled=true", "conductor.elasticsearch.version=6"}) + properties = {"conductor.indexing.enabled=true", "conductor.elasticsearch.version=7"}) public abstract class AbstractEndToEndTest { private static final Logger log = LoggerFactory.getLogger(AbstractEndToEndTest.class); diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index df19deae7..f66cfa3cd 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -12,9 +12,7 @@ */ package com.netflix.conductor.test.integration.grpc; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import org.junit.Test; import org.junit.runner.RunWith; @@ -114,7 +112,7 @@ public void testAll() throws Exception { assertEquals(taskName, def.getName()); } - WorkflowDef def = createWorkflowDefinition("test"); + WorkflowDef def = createWorkflowDefinition("test" + UUID.randomUUID()); WorkflowTask t0 = createWorkflowTask("t0"); WorkflowTask t1 = createWorkflowTask("t1"); From 508c9fb8e7ca51a8ff9fbc6bf9c70fe9f0aa21c4 Mon Sep 17 00:00:00 2001 From: boneys Date: Mon, 18 Dec 2023 23:48:35 -0800 Subject: [PATCH 049/202] Add the task update API by reference name --- .../core/dal/ExecutionDAOFacade.java | 6 +++- .../conductor/service/ExecutionService.java | 30 +++++++++++++++---- .../conductor/service/TaskService.java | 7 +++++ .../conductor/service/TaskServiceImpl.java | 21 +++++++++++++ .../rest/controllers/TaskResource.java | 12 ++++++++ 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java index 92255bc4d..24d0a93b9 100644 --- a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java +++ b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java @@ -439,11 +439,15 @@ public List createTasks(List tasks) { } public List getTasksForWorkflow(String workflowId) { - return executionDAO.getTasksForWorkflow(workflowId).stream() + return getTaskModelsForWorkflow(workflowId).stream() .map(TaskModel::toTask) .collect(Collectors.toList()); } + public List getTaskModelsForWorkflow(String workflowId) { + return executionDAO.getTasksForWorkflow(workflowId); + } + public TaskModel getTaskModel(String taskId) { TaskModel taskModel = getTaskFromDatastore(taskId); if (taskModel != null) { diff --git a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java index a4e411b4e..7a70eda72 100644 --- a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java +++ b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java @@ -14,6 +14,7 @@ import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -27,6 +28,7 @@ import com.netflix.conductor.common.utils.ExternalPayloadStorage; import com.netflix.conductor.common.utils.ExternalPayloadStorage.Operation; import com.netflix.conductor.common.utils.ExternalPayloadStorage.PayloadType; +import com.netflix.conductor.common.utils.TaskUtils; import com.netflix.conductor.core.config.ConductorProperties; import com.netflix.conductor.core.dal.ExecutionDAOFacade; import com.netflix.conductor.core.events.queue.Message; @@ -252,12 +254,28 @@ public Task getTask(String taskId) { } public Task getPendingTaskForWorkflow(String taskReferenceName, String workflowId) { - return executionDAOFacade.getTasksForWorkflow(workflowId).stream() - .filter(task -> !task.getStatus().isTerminal()) - .filter(task -> task.getReferenceTaskName().equals(taskReferenceName)) - .findFirst() // There can only be one task by a given reference name running at a - // time. - .orElse(null); + List tasks = executionDAOFacade.getTaskModelsForWorkflow(workflowId); + Stream taskStream = + tasks.stream().filter(task -> !task.getStatus().isTerminal()); + Optional found = + taskStream + .filter(task -> task.getReferenceTaskName().equals(taskReferenceName)) + .findFirst(); + if (found.isPresent()) { + return found.get().toTask(); + } + // If no task is found, let's check if there is one inside an iteration + found = + tasks.stream() + .filter(task -> !task.getStatus().isTerminal()) + .filter( + task -> + TaskUtils.removeIterationFromTaskRefName( + task.getReferenceTaskName()) + .equals(taskReferenceName)) + .findFirst(); + + return found.map(TaskModel::toTask).orElse(null); } /** diff --git a/core/src/main/java/com/netflix/conductor/service/TaskService.java b/core/src/main/java/com/netflix/conductor/service/TaskService.java index d89772659..e3d18e08e 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskService.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskService.java @@ -250,4 +250,11 @@ SearchResult search( */ ExternalStorageLocation getExternalStorageLocation( String path, String operation, String payloadType); + + String updateTask( + String workflowId, + String taskRefName, + TaskResult.Status status, + String workerId, + Map output); } diff --git a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java index 5c07d5cff..2f70487a4 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java @@ -139,6 +139,27 @@ public String updateTask(TaskResult taskResult) { return taskResult.getTaskId(); } + @Override + public String updateTask( + String workflowId, + String taskRefName, + TaskResult.Status status, + String workerId, + Map output) { + Task pending = getPendingTaskForWorkflow(workflowId, taskRefName); + if (pending == null) { + return null; + } + + TaskResult taskResult = new TaskResult(pending); + taskResult.setStatus(status); + taskResult.getOutputData().putAll(output); + if (StringUtils.isNotBlank(workerId)) { + taskResult.setWorkerId(workerId); + } + return updateTask(taskResult); + } + /** * Ack Task is received. * diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java index d9f818f44..500bed4a6 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java @@ -83,6 +83,18 @@ public String updateTask(@RequestBody TaskResult taskResult) { return taskService.updateTask(taskResult); } + @PostMapping(value = "/{workflowId}/{taskRefName}/{status}", produces = TEXT_PLAIN_VALUE) + @Operation(summary = "Update a task By Ref Name") + public String updateTask( + @PathVariable("workflowId") String workflowId, + @PathVariable("taskRefName") String taskRefName, + @PathVariable("status") TaskResult.Status status, + @RequestParam(value = "workerid", required = false) String workerId, + @RequestBody Map output) { + + return taskService.updateTask(workflowId, taskRefName, status, workerId, output); + } + @PostMapping("/{taskId}/log") @Operation(summary = "Log Task Execution Details") public void log(@PathVariable("taskId") String taskId, @RequestBody String log) { From 7dcb6c209b66785eaf273ba0dad0f30dace7e252 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 19 Dec 2023 00:13:27 -0800 Subject: [PATCH 050/202] test fixes --- LICENSE | 2 +- redis-lock/build.gradle | 2 +- .../conductor/redis/lock/RedisLockTest.java | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/LICENSE b/LICENSE index 6a1d025d8..78ae7ea62 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} Netflix, Inc. + Copyright {yyyy} Orkes, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/redis-lock/build.gradle b/redis-lock/build.gradle index bc3c074cb..d8a0feb0a 100644 --- a/redis-lock/build.gradle +++ b/redis-lock/build.gradle @@ -5,5 +5,5 @@ dependencies { implementation "org.apache.commons:commons-lang3" implementation "org.redisson:redisson:${revRedisson}" - testImplementation "com.github.kstyrc:embedded-redis:${revEmbeddedRedis}" + testImplementation "org.testcontainers:testcontainers:${revTestContainer}" } diff --git a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java index 7414b72c3..9a9cc3867 100644 --- a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java +++ b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java @@ -26,7 +26,7 @@ import com.netflix.conductor.redislock.config.RedisLockProperties.REDIS_SERVER_TYPE; import com.netflix.conductor.redislock.lock.RedisLock; -import redis.embedded.RedisServer; +import org.testcontainers.containers.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -38,16 +38,16 @@ public class RedisLockTest { private static RedisLock redisLock; private static Config config; private static RedissonClient redisson; - private static RedisServer redisServer = null; + + static GenericContainer redis = new GenericContainer("redis:5.0.3-alpine") + .withExposedPorts(6379); @BeforeClass public static void setUp() throws Exception { - String testServerAddress = "redis://127.0.0.1:6371"; - redisServer = new RedisServer(6371); - if (redisServer.isActive()) { - redisServer.stop(); - } - redisServer.start(); + redis.start(); + int port = redis.getFirstMappedPort(); + String host = redis.getHost(); + String testServerAddress = "redis://" + host + ":" + port; RedisLockProperties properties = mock(RedisLockProperties.class); when(properties.getServerType()).thenReturn(REDIS_SERVER_TYPE.SINGLE); @@ -68,7 +68,7 @@ public static void setUp() throws Exception { @AfterClass public static void tearDown() { - redisServer.stop(); + redis.stop(); } @Test From e8d17df0a6945ad93e6db2675808a536b7c6d65e Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 19 Dec 2023 01:06:22 -0800 Subject: [PATCH 051/202] Update RedisLockTest.java --- .../com/netflix/conductor/redis/lock/RedisLockTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java index 9a9cc3867..24953dd7b 100644 --- a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java +++ b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java @@ -21,13 +21,12 @@ import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; +import org.testcontainers.containers.*; import com.netflix.conductor.redislock.config.RedisLockProperties; import com.netflix.conductor.redislock.config.RedisLockProperties.REDIS_SERVER_TYPE; import com.netflix.conductor.redislock.lock.RedisLock; -import org.testcontainers.containers.*; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -39,8 +38,8 @@ public class RedisLockTest { private static Config config; private static RedissonClient redisson; - static GenericContainer redis = new GenericContainer("redis:5.0.3-alpine") - .withExposedPorts(6379); + static GenericContainer redis = + new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379); @BeforeClass public static void setUp() throws Exception { From fdf5f5e73839e196b472dd2d15a178e6dea29fc9 Mon Sep 17 00:00:00 2001 From: Iva Koleva Date: Tue, 19 Dec 2023 21:38:19 +0200 Subject: [PATCH 052/202] WorkflowTask permissive property added, so various task types can be permissive. --- .../common/metadata/tasks/TaskType.java | 2 - .../metadata/workflow/WorkflowTask.java | 19 +++ .../core/execution/DeciderService.java | 10 +- .../core/execution/WorkflowExecutor.java | 2 +- .../mapper/PermissiveTaskMapper.java | 102 ---------------- .../core/execution/tasks/ExclusiveJoin.java | 3 +- .../conductor/core/execution/tasks/Join.java | 3 +- .../core/execution/TestDeciderOutcomes.java | 5 +- .../mapper/PermissiveTaskMapperTest.java | 114 ------------------ .../conductor/grpc/AbstractProtoMapper.java | 2 + grpc/src/main/proto/model/workflowtask.proto | 1 + .../workflow/def/tasks/PermissiveTask.java | 47 -------- .../workflow/executor/WorkflowExecutor.java | 1 - .../test/integration/ForkJoinSpec.groovy | 4 +- ...fork_join_permissive_integration_test.json | 6 +- ...ermissive_task_retry_integration_test.json | 12 +- ...issive_optional_task_integration_test.json | 6 +- ...with_permissive_task_integration_test.json | 6 +- 18 files changed, 52 insertions(+), 293 deletions(-) delete mode 100644 core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java delete mode 100644 core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java delete mode 100644 java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java index efc985acd..235a0ac91 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -23,7 +23,6 @@ public enum TaskType { DYNAMIC, FORK_JOIN, FORK_JOIN_DYNAMIC, - PERMISSIVE, DECISION, SWITCH, JOIN, @@ -71,7 +70,6 @@ public enum TaskType { public static final String TASK_TYPE_JSON_JQ_TRANSFORM = "JSON_JQ_TRANSFORM"; public static final String TASK_TYPE_SET_VARIABLE = "SET_VARIABLE"; public static final String TASK_TYPE_FORK = "FORK"; - public static final String TASK_TYPE_PERMISSIVE = "PERMISSIVE"; public static final String TASK_TYPE_NOOP = "NOOP"; private static final Set BUILT_IN_TASKS = new HashSet<>(); diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java index 492a61d33..43b3c8af2 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -153,6 +153,9 @@ public void setTasks(List tasks) { @ProtoField(id = 28) private String expression; + @ProtoField(id = 29) + private boolean permissive = false; + /** * @return the name */ @@ -548,6 +551,22 @@ public void setExpression(String expression) { this.expression = expression; } + /** + * @return If the task is permissive. When set to true, and the task is in failed status, + * fail-fast does not occur. The workflow execution continues until reaching join or end of + * workflow, allowing idempotent execution of other tasks. + */ + public boolean isPermissive() { + return this.permissive; + } + + /** + * @param permissive when set to true, the task is marked as permissive + */ + public void setPermissive(boolean permissive) { + this.permissive = permissive; + } + private Collection> children() { Collection> workflowTaskLists = new LinkedList<>(); diff --git a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java index 52afc52c2..127440d60 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java @@ -44,7 +44,6 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; -import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TERMINATE; import static com.netflix.conductor.common.metadata.tasks.TaskType.USER_DEFINED; import static com.netflix.conductor.model.TaskModel.Status.*; @@ -209,9 +208,7 @@ private DeciderOutcome decide(final WorkflowModel workflow, List preS executedTaskRefNames.remove(retryTask.get().getReferenceTaskName()); outcome.tasksToBeUpdated.add(pendingTask); } else if (!(pendingTask.getWorkflowTask() != null - && TaskType.PERMISSIVE - .name() - .equals(pendingTask.getWorkflowTask().getType()) + && pendingTask.getWorkflowTask().isPermissive() && !pendingTask.getWorkflowTask().isOptional())) { pendingTask.setStatus(COMPLETED_WITH_ERRORS); } @@ -262,7 +259,7 @@ private DeciderOutcome decide(final WorkflowModel workflow, List preS List permissiveTasksTerminalNonSuccessful = workflow.getTasks().stream() .filter(t -> t.getWorkflowTask() != null) - .filter(t -> PERMISSIVE.name().equals(t.getWorkflowTask().getType())) + .filter(t -> t.getWorkflowTask().isPermissive()) .filter(t -> !t.getWorkflowTask().isOptional()) .collect( Collectors.toMap( @@ -563,8 +560,7 @@ Optional retry( || TaskType.isBuiltIn(task.getTaskType()) || expectedRetryCount <= retryCount) { if (workflowTask != null - && (workflowTask.isOptional() - || TaskType.PERMISSIVE.name().equals(workflowTask.getType()))) { + && (workflowTask.isOptional() || workflowTask.isPermissive())) { return Optional.empty(); } WorkflowModel.Status status; diff --git a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java index 37bc358bb..8cb13f1b6 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java @@ -1832,7 +1832,7 @@ private static boolean isJoinOnFailedPermissive(List joinOn, WorkflowMod .map(workflow::getTaskByRefName) .anyMatch( t -> - TaskType.PERMISSIVE.name().equals(t.getWorkflowTask().getType()) + t.getWorkflowTask().isPermissive() && !t.getWorkflowTask().isOptional() && t.getStatus().equals(FAILED)); } diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java deleted file mode 100644 index 124a9b2e0..000000000 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapper.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2023 Netflix, Inc. - *

- * 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 com.netflix.conductor.core.execution.mapper; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import com.netflix.conductor.common.metadata.tasks.TaskDef; -import com.netflix.conductor.common.metadata.tasks.TaskType; -import com.netflix.conductor.common.metadata.workflow.WorkflowDef; -import com.netflix.conductor.common.metadata.workflow.WorkflowTask; -import com.netflix.conductor.core.exception.TerminateWorkflowException; -import com.netflix.conductor.core.utils.ParametersUtils; -import com.netflix.conductor.model.TaskModel; -import com.netflix.conductor.model.WorkflowModel; - -/** - * An implementation of {@link TaskMapper} to map a {@link WorkflowTask} of type {@link - * TaskType#PERMISSIVE} to a {@link TaskModel} with status {@link TaskModel.Status#SCHEDULED}. - */ -@Component -public class PermissiveTaskMapper implements TaskMapper { - - public static final Logger LOGGER = LoggerFactory.getLogger(PermissiveTaskMapper.class); - private final ParametersUtils parametersUtils; - - public PermissiveTaskMapper(ParametersUtils parametersUtils) { - this.parametersUtils = parametersUtils; - } - - @Override - public String getTaskType() { - return TaskType.PERMISSIVE.name(); - } - - /** - * This method maps a {@link WorkflowTask} of type {@link TaskType#PERMISSIVE} to a {@link - * TaskModel} - * - * @param taskMapperContext: A wrapper class containing the {@link WorkflowTask}, {@link - * WorkflowDef}, {@link WorkflowModel} and a string representation of the TaskId - * @return a List with just one exclusive task - * @throws TerminateWorkflowException In case if the task definition does not exist - */ - @Override - public List getMappedTasks(TaskMapperContext taskMapperContext) - throws TerminateWorkflowException { - - LOGGER.debug("TaskMapperContext {} in PermissiveTaskMapper", taskMapperContext); - - WorkflowTask workflowTask = taskMapperContext.getWorkflowTask(); - WorkflowModel workflowModel = taskMapperContext.getWorkflowModel(); - int retryCount = taskMapperContext.getRetryCount(); - String retriedTaskId = taskMapperContext.getRetryTaskId(); - - TaskDef taskDefinition = - Optional.ofNullable(workflowTask.getTaskDefinition()) - .orElseThrow( - () -> { - String reason = - String.format( - "Invalid task. Task %s does not have a definition", - workflowTask.getName()); - return new TerminateWorkflowException(reason); - }); - - Map input = - parametersUtils.getTaskInput( - workflowTask.getInputParameters(), - workflowModel, - taskDefinition, - taskMapperContext.getTaskId()); - TaskModel permissiveTask = taskMapperContext.createTaskModel(); - permissiveTask.setTaskType(workflowTask.getName()); - permissiveTask.setStartDelayInSeconds(workflowTask.getStartDelay()); - permissiveTask.setInputData(input); - permissiveTask.setStatus(TaskModel.Status.SCHEDULED); - permissiveTask.setRetryCount(retryCount); - permissiveTask.setCallbackAfterSeconds(workflowTask.getStartDelay()); - permissiveTask.setResponseTimeoutSeconds(taskDefinition.getResponseTimeoutSeconds()); - permissiveTask.setRetriedTaskId(retriedTaskId); - permissiveTask.setRateLimitPerFrequency(taskDefinition.getRateLimitPerFrequency()); - permissiveTask.setRateLimitFrequencyInSeconds( - taskDefinition.getRateLimitFrequencyInSeconds()); - return List.of(permissiveTask); - } -} diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java index 62a02c3c9..7484a0644 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java @@ -24,7 +24,6 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; -import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_EXCLUSIVE_JOIN; @Component(TASK_TYPE_EXCLUSIVE_JOIN) @@ -68,7 +67,7 @@ public boolean execute( foundExlusiveJoinOnTask = taskStatus.isTerminal(); hasFailures = !taskStatus.isSuccessful() - && (!PERMISSIVE.name().equals(exclusiveTask.getWorkflowTask().getType()) + && (!exclusiveTask.getWorkflowTask().isPermissive() || joinOn.stream() .map(workflow::getTaskByRefName) .allMatch(t -> t.getStatus().isTerminal())); diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java index 14fdba6f1..e1646cf04 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java @@ -23,7 +23,6 @@ import com.netflix.conductor.model.TaskModel; import com.netflix.conductor.model.WorkflowModel; -import static com.netflix.conductor.common.metadata.tasks.TaskType.PERMISSIVE; import static com.netflix.conductor.common.metadata.tasks.TaskType.TASK_TYPE_JOIN; @Component(TASK_TYPE_JOIN) @@ -61,7 +60,7 @@ public boolean execute( hasFailures = !taskStatus.isSuccessful() && !forkedTask.getWorkflowTask().isOptional() - && (!PERMISSIVE.name().equals(forkedTask.getWorkflowTask().getType()) + && (!forkedTask.getWorkflowTask().isPermissive() || joinOn.stream() .map(workflow::getTaskByRefName) .allMatch(t -> t.getStatus().isTerminal())); diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java index ca06c8ea8..8362fded1 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java @@ -440,7 +440,6 @@ public void testOptional() { outcome.tasksToBeScheduled.get(0).getReferenceTaskName()); } - /** Similar to {@link #testOptional} */ @Test public void testPermissive() { WorkflowDef def = new WorkflowDef(); @@ -448,14 +447,14 @@ public void testPermissive() { WorkflowTask task1 = new WorkflowTask(); task1.setName("task0"); - task1.setType("PERMISSIVE"); + task1.setPermissive(true); task1.setTaskReferenceName("t0"); task1.getInputParameters().put("taskId", "${CPEWF_TASK_ID}"); task1.setTaskDefinition(new TaskDef("task0")); WorkflowTask task2 = new WorkflowTask(); task2.setName("task1"); - task2.setType("PERMISSIVE"); + task2.setPermissive(true); task2.setTaskReferenceName("t1"); task2.setTaskDefinition(new TaskDef("task1")); diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java deleted file mode 100644 index 97920a3f0..000000000 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/PermissiveTaskMapperTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2023 Netflix, Inc. - *

- * 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 com.netflix.conductor.core.execution.mapper; - -import java.util.HashMap; -import java.util.List; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import com.netflix.conductor.common.metadata.tasks.TaskDef; -import com.netflix.conductor.common.metadata.workflow.WorkflowDef; -import com.netflix.conductor.common.metadata.workflow.WorkflowTask; -import com.netflix.conductor.core.exception.TerminateWorkflowException; -import com.netflix.conductor.core.utils.IDGenerator; -import com.netflix.conductor.core.utils.ParametersUtils; -import com.netflix.conductor.model.TaskModel; -import com.netflix.conductor.model.WorkflowModel; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.*; - -public class PermissiveTaskMapperTest { - - private PermissiveTaskMapper permissiveTaskMapper; - - private IDGenerator idGenerator = new IDGenerator(); - - @Rule public ExpectedException expectedException = ExpectedException.none(); - - @Before - public void setUp() { - ParametersUtils parametersUtils = mock(ParametersUtils.class); - permissiveTaskMapper = new PermissiveTaskMapper(parametersUtils); - } - - @Test - public void getMappedTasks() { - - WorkflowTask workflowTask = new WorkflowTask(); - workflowTask.setName("permissive_task"); - workflowTask.setTaskDefinition(new TaskDef("permissive_task")); - - String taskId = idGenerator.generate(); - String retriedTaskId = idGenerator.generate(); - - WorkflowDef workflowDef = new WorkflowDef(); - WorkflowModel workflow = new WorkflowModel(); - workflow.setWorkflowDefinition(workflowDef); - - TaskMapperContext taskMapperContext = - TaskMapperContext.newBuilder() - .withWorkflowModel(workflow) - .withTaskDefinition(new TaskDef()) - .withWorkflowTask(workflowTask) - .withTaskInput(new HashMap<>()) - .withRetryCount(0) - .withRetryTaskId(retriedTaskId) - .withTaskId(taskId) - .build(); - - List mappedTasks = permissiveTaskMapper.getMappedTasks(taskMapperContext); - assertNotNull(mappedTasks); - assertEquals(1, mappedTasks.size()); - } - - @Test - public void getMappedTasksException() { - - // Given - WorkflowTask workflowTask = new WorkflowTask(); - workflowTask.setName("permissive_task"); - String taskId = idGenerator.generate(); - String retriedTaskId = idGenerator.generate(); - - WorkflowDef workflowDef = new WorkflowDef(); - WorkflowModel workflow = new WorkflowModel(); - workflow.setWorkflowDefinition(workflowDef); - - TaskMapperContext taskMapperContext = - TaskMapperContext.newBuilder() - .withWorkflowModel(workflow) - .withTaskDefinition(new TaskDef()) - .withWorkflowTask(workflowTask) - .withTaskInput(new HashMap<>()) - .withRetryCount(0) - .withRetryTaskId(retriedTaskId) - .withTaskId(taskId) - .build(); - - // then - expectedException.expect(TerminateWorkflowException.class); - expectedException.expectMessage( - String.format( - "Invalid task. Task %s does not have a definition", - workflowTask.getName())); - - // when - permissiveTaskMapper.getMappedTasks(taskMapperContext); - } -} diff --git a/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java b/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java index c9cd06e38..113059aac 100644 --- a/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java +++ b/grpc/src/main/java/com/netflix/conductor/grpc/AbstractProtoMapper.java @@ -1351,6 +1351,7 @@ public WorkflowTaskPb.WorkflowTask toProto(WorkflowTask from) { if (from.getExpression() != null) { to.setExpression( from.getExpression() ); } + to.setPermissive( from.isPermissive() ); return to.build(); } @@ -1396,6 +1397,7 @@ public WorkflowTask fromProto(WorkflowTaskPb.WorkflowTask from) { to.setRetryCount( from.getRetryCount() ); to.setEvaluatorType( from.getEvaluatorType() ); to.setExpression( from.getExpression() ); + to.setPermissive( from.getPermissive() ); return to; } diff --git a/grpc/src/main/proto/model/workflowtask.proto b/grpc/src/main/proto/model/workflowtask.proto index 8855a714f..2c35d56dd 100644 --- a/grpc/src/main/proto/model/workflowtask.proto +++ b/grpc/src/main/proto/model/workflowtask.proto @@ -41,4 +41,5 @@ message WorkflowTask { int32 retry_count = 26; string evaluator_type = 27; string expression = 28; + bool permissive = 29; } diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java deleted file mode 100644 index f767f8ea5..000000000 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/PermissiveTask.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2023 Netflix, Inc. - *

- * 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 com.netflix.conductor.sdk.workflow.def.tasks; - -import com.netflix.conductor.common.metadata.tasks.TaskDef; -import com.netflix.conductor.common.metadata.tasks.TaskType; -import com.netflix.conductor.common.metadata.workflow.WorkflowTask; - -/* Workflow permissive task executed by a worker */ -public class PermissiveTask extends Task { - - private TaskDef taskDef; - - public PermissiveTask(String taskDefName, String taskReferenceName) { - super(taskReferenceName, TaskType.PERMISSIVE); - super.name(taskDefName); - } - - PermissiveTask(WorkflowTask workflowTask) { - super(workflowTask); - this.taskDef = workflowTask.getTaskDefinition(); - } - - public TaskDef getTaskDef() { - return taskDef; - } - - public PermissiveTask setTaskDef(TaskDef taskDef) { - this.taskDef = taskDef; - return this; - } - - @Override - protected void updateWorkflowTask(WorkflowTask workflowTask) { - workflowTask.setTaskDefinition(taskDef); - } -} diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java index 6601f1a23..ae75b6c83 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -72,7 +72,6 @@ public static void initTaskImplementations() { TaskRegistry.register(TaskType.DYNAMIC.name(), Dynamic.class); TaskRegistry.register(TaskType.FORK_JOIN_DYNAMIC.name(), DynamicFork.class); TaskRegistry.register(TaskType.FORK_JOIN.name(), ForkJoin.class); - TaskRegistry.register(TaskType.PERMISSIVE.name(), PermissiveTask.class); TaskRegistry.register(TaskType.HTTP.name(), Http.class); TaskRegistry.register(TaskType.INLINE.name(), Javascript.class); TaskRegistry.register(TaskType.JOIN.name(), Join.class); diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy index b6540b160..65cce3265 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy @@ -291,10 +291,10 @@ class ForkJoinSpec extends AbstractSpecification { tasks.size() == 4 tasks[0].status == Task.Status.COMPLETED tasks[0].taskType == 'FORK' - tasks[1].workflowTask.type == TaskType.PERMISSIVE.name() + tasks[1].workflowTask.permissive tasks[1].status == Task.Status.SCHEDULED tasks[1].taskType == 'integration_task_1' - tasks[2].workflowTask.type == TaskType.PERMISSIVE.name() + tasks[2].workflowTask.permissive tasks[2].status == Task.Status.SCHEDULED tasks[2].taskType == 'integration_task_2' tasks[3].status == Task.Status.IN_PROGRESS diff --git a/test-harness/src/test/resources/fork_join_permissive_integration_test.json b/test-harness/src/test/resources/fork_join_permissive_integration_test.json index 6f65d4e11..771230d20 100644 --- a/test-harness/src/test/resources/fork_join_permissive_integration_test.json +++ b/test-harness/src/test/resources/fork_join_permissive_integration_test.json @@ -19,13 +19,14 @@ "p1": "workflow.input.param1", "p2": "workflow.input.param2" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [] @@ -38,13 +39,14 @@ "inputParameters": { "tp1": "workflow.input.param1" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [] diff --git a/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json b/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json index 8c1ebef77..ead2a678c 100644 --- a/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json +++ b/test-harness/src/test/resources/fork_join_with_no_permissive_task_retry_integration_test.json @@ -19,13 +19,14 @@ "p1": "workflow.input.param1", "p2": "workflow.input.param2" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [], @@ -53,13 +54,14 @@ "p1": "workflow.input.param1", "p2": "workflow.input.param2" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [], @@ -88,13 +90,14 @@ "inputParameters": { "tp1": "workflow.input.param1" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [], @@ -148,13 +151,14 @@ "inputParameters": { "tp1": "workflow.input.param1" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [], diff --git a/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json b/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json index 84c6910ab..ac68e097c 100644 --- a/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json +++ b/test-harness/src/test/resources/simple_workflow_with_permissive_optional_task_integration_test.json @@ -10,13 +10,14 @@ "p1": "${workflow.input.param1}", "p2": "${workflow.input.param2}" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": true, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [] @@ -28,13 +29,14 @@ "tp1": "${workflow.input.param1}", "tp2": "${t1.output.op}" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "defaultExclusiveJoinTask": [], "asyncComplete": false, "loopOver": [] diff --git a/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json b/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json index 2b8f4dbfe..9893d3a26 100644 --- a/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json +++ b/test-harness/src/test/resources/simple_workflow_with_permissive_task_integration_test.json @@ -10,13 +10,14 @@ "p1": "${workflow.input.param1}", "p2": "${workflow.input.param2}" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "retryCount": 1, "taskDefinition": { "createdBy": "integration_app", @@ -45,13 +46,14 @@ "tp1": "${workflow.input.param1}", "tp2": "${t1.output.op}" }, - "type": "PERMISSIVE", + "type": "SIMPLE", "decisionCases": {}, "defaultCase": [], "forkTasks": [], "startDelay": 0, "joinOn": [], "optional": false, + "permissive": true, "retryCount": 1, "taskDefinition": { "createdBy": "integration_app", From 7128d66a49a0b83188b3a29fdc66558d1c278059 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 19 Dec 2023 13:06:38 -0800 Subject: [PATCH 053/202] Update SECURITY.md --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 80978ee5b..ecb1b4087 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,4 +10,4 @@ ## Reporting a Vulnerability -Please email conductor@netflix.com to report vulnerabilities. +Please email oess@orkes.com to report vulnerabilities. From a444e23d6cb5896b20a570ef400a90a9eac2ab62 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Tue, 19 Dec 2023 13:06:54 -0800 Subject: [PATCH 054/202] Update SECURITY.md --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index ecb1b4087..7afa91969 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,4 +10,4 @@ ## Reporting a Vulnerability -Please email oess@orkes.com to report vulnerabilities. +Please email oss@orkes.com to report vulnerabilities. From 71b2b5731d11b70dd304179df5737e8f7bf09334 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:20:22 -0800 Subject: [PATCH 055/202] license header changes --- README.md | 6 +++--- .../conductor/contribs/queue/amqp/AMQPConnection.java | 2 +- .../conductor/contribs/queue/amqp/AMQPObservableQueue.java | 2 +- .../queue/amqp/config/AMQPEventQueueConfiguration.java | 2 +- .../queue/amqp/config/AMQPEventQueueProperties.java | 2 +- .../contribs/queue/amqp/config/AMQPEventQueueProvider.java | 2 +- .../contribs/queue/amqp/config/AMQPRetryPattern.java | 2 +- .../contribs/queue/amqp/util/AMQPConfigurations.java | 2 +- .../conductor/contribs/queue/amqp/util/AMQPConstants.java | 2 +- .../conductor/contribs/queue/amqp/util/AMQPSettings.java | 2 +- .../conductor/contribs/queue/amqp/util/ConnectionType.java | 2 +- .../conductor/contribs/queue/amqp/util/RetryType.java | 2 +- .../contribs/queue/amqp/AMQPEventQueueProviderTest.java | 2 +- .../contribs/queue/amqp/AMQPObservableQueueTest.java | 2 +- .../conductor/contribs/queue/amqp/AMQPSettingsTest.java | 2 +- .../src/example/java/com/example/Example.java | 2 +- .../annotationsprocessor/protogen/AbstractMessage.java | 2 +- .../conductor/annotationsprocessor/protogen/Enum.java | 2 +- .../conductor/annotationsprocessor/protogen/Message.java | 2 +- .../conductor/annotationsprocessor/protogen/ProtoFile.java | 2 +- .../conductor/annotationsprocessor/protogen/ProtoGen.java | 2 +- .../annotationsprocessor/protogen/ProtoGenTask.java | 2 +- .../annotationsprocessor/protogen/types/AbstractType.java | 2 +- .../protogen/types/ExternMessageType.java | 2 +- .../annotationsprocessor/protogen/types/GenericType.java | 2 +- .../annotationsprocessor/protogen/types/ListType.java | 2 +- .../annotationsprocessor/protogen/types/MapType.java | 2 +- .../annotationsprocessor/protogen/types/MessageType.java | 2 +- .../annotationsprocessor/protogen/types/ScalarType.java | 2 +- .../annotationsprocessor/protogen/types/TypeMapper.java | 2 +- .../annotationsprocessor/protogen/types/WrappedType.java | 2 +- .../annotationsprocessor/protogen/ProtoGenTest.java | 2 +- .../netflix/conductor/annotations/protogen/ProtoEnum.java | 2 +- .../netflix/conductor/annotations/protogen/ProtoField.java | 2 +- .../conductor/annotations/protogen/ProtoMessage.java | 2 +- .../com/netflix/conductor/s3/config/S3Configuration.java | 2 +- .../java/com/netflix/conductor/s3/config/S3Properties.java | 2 +- .../com/netflix/conductor/s3/storage/S3PayloadStorage.java | 2 +- .../conductor/sqs/config/SQSEventQueueConfiguration.java | 2 +- .../conductor/sqs/config/SQSEventQueueProperties.java | 2 +- .../netflix/conductor/sqs/config/SQSEventQueueProvider.java | 2 +- .../conductor/sqs/eventqueue/SQSObservableQueue.java | 2 +- .../sqs/eventqueue/DefaultEventQueueProcessorTest.java | 2 +- .../conductor/sqs/eventqueue/SQSObservableQueueTest.java | 2 +- .../conductor/azureblob/config/AzureBlobConfiguration.java | 2 +- .../conductor/azureblob/config/AzureBlobProperties.java | 2 +- .../azureblob/storage/AzureBlobPayloadStorage.java | 2 +- .../azureblob/storage/AzureBlobPayloadStorageTest.java | 2 +- .../conductor/cassandra/config/CassandraConfiguration.java | 2 +- .../conductor/cassandra/config/CassandraProperties.java | 2 +- .../cassandra/config/cache/CacheableEventHandlerDAO.java | 2 +- .../cassandra/config/cache/CacheableMetadataDAO.java | 2 +- .../conductor/cassandra/config/cache/CachingConfig.java | 2 +- .../netflix/conductor/cassandra/dao/CassandraBaseDAO.java | 2 +- .../conductor/cassandra/dao/CassandraEventHandlerDAO.java | 2 +- .../conductor/cassandra/dao/CassandraExecutionDAO.java | 2 +- .../conductor/cassandra/dao/CassandraMetadataDAO.java | 2 +- .../conductor/cassandra/dao/CassandraPollDataDAO.java | 2 +- .../com/netflix/conductor/cassandra/util/Constants.java | 2 +- .../com/netflix/conductor/cassandra/util/Statements.java | 2 +- .../cassandra/dao/CassandraEventHandlerDAOSpec.groovy | 2 +- .../cassandra/dao/CassandraExecutionDAOSpec.groovy | 2 +- .../conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy | 2 +- .../netflix/conductor/cassandra/dao/CassandraSpec.groovy | 2 +- .../netflix/conductor/cassandra/util/StatementsSpec.groovy | 2 +- .../netflix/conductor/client/spring/ClientProperties.java | 2 +- .../client/spring/ConductorClientAutoConfiguration.java | 2 +- .../client/spring/ConductorWorkerAutoConfiguration.java | 2 +- .../conductor/client/spring/SpringWorkerConfiguration.java | 2 +- .../com/netflix/conductor/client/spring/ExampleClient.java | 2 +- .../java/com/netflix/conductor/client/spring/Workers.java | 2 +- .../conductor/client/automator/PollingSemaphore.java | 2 +- .../conductor/client/automator/TaskPollExecutor.java | 2 +- .../conductor/client/automator/TaskRunnerConfigurer.java | 2 +- .../client/config/ConductorClientConfiguration.java | 2 +- .../client/config/DefaultConductorClientConfiguration.java | 2 +- .../netflix/conductor/client/config/PropertyFactory.java | 2 +- .../client/exception/ConductorClientException.java | 2 +- .../java/com/netflix/conductor/client/http/ClientBase.java | 2 +- .../netflix/conductor/client/http/ClientRequestHandler.java | 2 +- .../java/com/netflix/conductor/client/http/EventClient.java | 2 +- .../com/netflix/conductor/client/http/MetadataClient.java | 2 +- .../com/netflix/conductor/client/http/PayloadStorage.java | 2 +- .../java/com/netflix/conductor/client/http/TaskClient.java | 2 +- .../com/netflix/conductor/client/http/WorkflowClient.java | 2 +- .../conductor/client/telemetry/MetricsContainer.java | 2 +- .../java/com/netflix/conductor/client/worker/Worker.java | 2 +- .../conductor/client/http/ClientSpecification.groovy | 2 +- .../netflix/conductor/client/http/EventClientSpec.groovy | 2 +- .../netflix/conductor/client/http/MetadataClientSpec.groovy | 2 +- .../com/netflix/conductor/client/http/TaskClientSpec.groovy | 2 +- .../netflix/conductor/client/http/WorkflowClientSpec.groovy | 2 +- .../conductor/client/automator/PollingSemaphoreTest.java | 2 +- .../conductor/client/automator/TaskPollExecutorTest.java | 2 +- .../client/automator/TaskRunnerConfigurerTest.java | 2 +- .../conductor/client/config/TestPropertyFactory.java | 2 +- .../test/java/com/netflix/conductor/client/sample/Main.java | 2 +- .../com/netflix/conductor/client/sample/SampleWorker.java | 2 +- .../conductor/client/testing/AbstractWorkflowTests.java | 2 +- .../netflix/conductor/client/testing/LoanWorkflowInput.java | 2 +- .../netflix/conductor/client/testing/LoanWorkflowTest.java | 2 +- .../netflix/conductor/client/testing/RegressionTest.java | 2 +- .../netflix/conductor/client/testing/SubWorkflowTest.java | 2 +- .../netflix/conductor/client/worker/TestWorkflowTask.java | 2 +- .../java/com/netflix/conductor/dao/ExecutionDAOTest.java | 2 +- .../src/test/java/com/netflix/conductor/dao/TestBase.java | 2 +- .../common/config/ObjectMapperBuilderConfiguration.java | 2 +- .../conductor/common/config/ObjectMapperConfiguration.java | 2 +- .../conductor/common/config/ObjectMapperProvider.java | 2 +- .../conductor/common/constraints/NoSemiColonConstraint.java | 2 +- .../common/constraints/OwnerEmailMandatoryConstraint.java | 2 +- .../constraints/TaskReferenceNameUniqueConstraint.java | 2 +- .../conductor/common/constraints/TaskTimeoutConstraint.java | 2 +- .../netflix/conductor/common/jackson/JsonProtoModule.java | 2 +- .../com/netflix/conductor/common/metadata/Auditable.java | 2 +- .../java/com/netflix/conductor/common/metadata/BaseDef.java | 2 +- .../netflix/conductor/common/metadata/acl/Permission.java | 2 +- .../conductor/common/metadata/events/EventExecution.java | 2 +- .../conductor/common/metadata/events/EventHandler.java | 2 +- .../netflix/conductor/common/metadata/tasks/PollData.java | 2 +- .../com/netflix/conductor/common/metadata/tasks/Task.java | 2 +- .../netflix/conductor/common/metadata/tasks/TaskDef.java | 2 +- .../conductor/common/metadata/tasks/TaskExecLog.java | 2 +- .../netflix/conductor/common/metadata/tasks/TaskResult.java | 2 +- .../netflix/conductor/common/metadata/tasks/TaskType.java | 2 +- .../common/metadata/workflow/DynamicForkJoinTask.java | 2 +- .../common/metadata/workflow/DynamicForkJoinTaskList.java | 2 +- .../common/metadata/workflow/RerunWorkflowRequest.java | 2 +- .../conductor/common/metadata/workflow/SkipTaskRequest.java | 2 +- .../common/metadata/workflow/StartWorkflowRequest.java | 2 +- .../common/metadata/workflow/SubWorkflowParams.java | 2 +- .../conductor/common/metadata/workflow/WorkflowDef.java | 2 +- .../common/metadata/workflow/WorkflowDefSummary.java | 2 +- .../conductor/common/metadata/workflow/WorkflowTask.java | 2 +- .../com/netflix/conductor/common/model/BulkResponse.java | 2 +- .../conductor/common/run/ExternalStorageLocation.java | 2 +- .../java/com/netflix/conductor/common/run/SearchResult.java | 2 +- .../java/com/netflix/conductor/common/run/TaskSummary.java | 2 +- .../java/com/netflix/conductor/common/run/Workflow.java | 2 +- .../com/netflix/conductor/common/run/WorkflowSummary.java | 2 +- .../netflix/conductor/common/run/WorkflowTestRequest.java | 2 +- .../netflix/conductor/common/utils/ConstraintParamUtil.java | 2 +- .../java/com/netflix/conductor/common/utils/EnvUtils.java | 2 +- .../conductor/common/utils/ExternalPayloadStorage.java | 2 +- .../com/netflix/conductor/common/utils/SummaryUtil.java | 2 +- .../java/com/netflix/conductor/common/utils/TaskUtils.java | 2 +- .../netflix/conductor/common/validation/ErrorResponse.java | 2 +- .../conductor/common/validation/ValidationError.java | 2 +- .../common/config/TestObjectMapperConfiguration.java | 2 +- .../netflix/conductor/common/events/EventHandlerTest.java | 2 +- .../com/netflix/conductor/common/run/TaskSummaryTest.java | 2 +- .../com/netflix/conductor/common/tasks/TaskDefTest.java | 2 +- .../com/netflix/conductor/common/tasks/TaskResultTest.java | 2 +- .../java/com/netflix/conductor/common/tasks/TaskTest.java | 2 +- .../conductor/common/utils/ConstraintParamUtilTest.java | 2 +- .../com/netflix/conductor/common/utils/SummaryUtilTest.java | 2 +- .../conductor/common/workflow/SubWorkflowParamsTest.java | 2 +- .../conductor/common/workflow/WorkflowDefValidatorTest.java | 2 +- .../netflix/conductor/common/workflow/WorkflowTaskTest.java | 2 +- .../main/java/com/netflix/conductor/annotations/Audit.java | 2 +- .../main/java/com/netflix/conductor/annotations/Trace.java | 2 +- .../netflix/conductor/annotations/VisibleForTesting.java | 2 +- .../com/netflix/conductor/core/LifecycleAwareComponent.java | 2 +- .../java/com/netflix/conductor/core/WorkflowContext.java | 2 +- .../conductor/core/config/ConductorCoreConfiguration.java | 2 +- .../netflix/conductor/core/config/ConductorProperties.java | 2 +- .../conductor/core/config/SchedulerConfiguration.java | 2 +- .../com/netflix/conductor/core/dal/ExecutionDAOFacade.java | 2 +- .../netflix/conductor/core/event/WorkflowCreationEvent.java | 2 +- .../conductor/core/event/WorkflowEvaluationEvent.java | 2 +- .../com/netflix/conductor/core/events/ActionProcessor.java | 2 +- .../conductor/core/events/DefaultEventProcessor.java | 2 +- .../conductor/core/events/DefaultEventQueueManager.java | 2 +- .../netflix/conductor/core/events/EventQueueManager.java | 2 +- .../netflix/conductor/core/events/EventQueueProvider.java | 2 +- .../java/com/netflix/conductor/core/events/EventQueues.java | 2 +- .../com/netflix/conductor/core/events/ScriptEvaluator.java | 2 +- .../conductor/core/events/SimpleActionProcessor.java | 2 +- .../core/events/queue/ConductorEventQueueProvider.java | 2 +- .../core/events/queue/ConductorObservableQueue.java | 2 +- .../core/events/queue/DefaultEventQueueProcessor.java | 2 +- .../com/netflix/conductor/core/events/queue/Message.java | 2 +- .../conductor/core/events/queue/ObservableQueue.java | 2 +- .../netflix/conductor/core/exception/ConflictException.java | 2 +- .../conductor/core/exception/NonTransientException.java | 2 +- .../netflix/conductor/core/exception/NotFoundException.java | 2 +- .../core/exception/TerminateWorkflowException.java | 2 +- .../conductor/core/exception/TransientException.java | 2 +- .../conductor/core/execution/AsyncSystemTaskExecutor.java | 2 +- .../netflix/conductor/core/execution/DeciderService.java | 2 +- .../conductor/core/execution/StartWorkflowInput.java | 2 +- .../netflix/conductor/core/execution/WorkflowExecutor.java | 2 +- .../conductor/core/execution/evaluators/Evaluator.java | 2 +- .../core/execution/evaluators/JavascriptEvaluator.java | 2 +- .../core/execution/evaluators/ValueParamEvaluator.java | 2 +- .../conductor/core/execution/mapper/DecisionTaskMapper.java | 2 +- .../conductor/core/execution/mapper/DoWhileTaskMapper.java | 2 +- .../conductor/core/execution/mapper/DynamicTaskMapper.java | 2 +- .../conductor/core/execution/mapper/EventTaskMapper.java | 2 +- .../core/execution/mapper/ExclusiveJoinTaskMapper.java | 2 +- .../core/execution/mapper/ForkJoinDynamicTaskMapper.java | 2 +- .../conductor/core/execution/mapper/ForkJoinTaskMapper.java | 2 +- .../conductor/core/execution/mapper/HTTPTaskMapper.java | 2 +- .../conductor/core/execution/mapper/HumanTaskMapper.java | 2 +- .../conductor/core/execution/mapper/InlineTaskMapper.java | 2 +- .../conductor/core/execution/mapper/JoinTaskMapper.java | 2 +- .../core/execution/mapper/JsonJQTransformTaskMapper.java | 2 +- .../core/execution/mapper/KafkaPublishTaskMapper.java | 2 +- .../conductor/core/execution/mapper/LambdaTaskMapper.java | 2 +- .../conductor/core/execution/mapper/NoopTaskMapper.java | 2 +- .../core/execution/mapper/SetVariableTaskMapper.java | 2 +- .../conductor/core/execution/mapper/SimpleTaskMapper.java | 2 +- .../core/execution/mapper/StartWorkflowTaskMapper.java | 2 +- .../core/execution/mapper/SubWorkflowTaskMapper.java | 2 +- .../conductor/core/execution/mapper/SwitchTaskMapper.java | 2 +- .../netflix/conductor/core/execution/mapper/TaskMapper.java | 2 +- .../conductor/core/execution/mapper/TaskMapperContext.java | 2 +- .../core/execution/mapper/TerminateTaskMapper.java | 2 +- .../core/execution/mapper/UserDefinedTaskMapper.java | 2 +- .../conductor/core/execution/mapper/WaitTaskMapper.java | 2 +- .../netflix/conductor/core/execution/tasks/Decision.java | 2 +- .../com/netflix/conductor/core/execution/tasks/DoWhile.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Event.java | 2 +- .../conductor/core/execution/tasks/ExclusiveJoin.java | 2 +- .../conductor/core/execution/tasks/ExecutionConfig.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Fork.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Human.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Inline.java | 2 +- .../core/execution/tasks/IsolatedTaskQueueProducer.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Join.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Lambda.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Noop.java | 2 +- .../netflix/conductor/core/execution/tasks/SetVariable.java | 2 +- .../conductor/core/execution/tasks/StartWorkflow.java | 2 +- .../netflix/conductor/core/execution/tasks/SubWorkflow.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Switch.java | 2 +- .../conductor/core/execution/tasks/SystemTaskRegistry.java | 2 +- .../conductor/core/execution/tasks/SystemTaskWorker.java | 2 +- .../core/execution/tasks/SystemTaskWorkerCoordinator.java | 2 +- .../netflix/conductor/core/execution/tasks/Terminate.java | 2 +- .../com/netflix/conductor/core/execution/tasks/Wait.java | 2 +- .../conductor/core/execution/tasks/WorkflowSystemTask.java | 2 +- .../java/com/netflix/conductor/core/index/NoopIndexDAO.java | 2 +- .../conductor/core/index/NoopIndexDAOConfiguration.java | 2 +- .../netflix/conductor/core/listener/TaskStatusListener.java | 2 +- .../conductor/core/listener/TaskStatusListenerStub.java | 2 +- .../conductor/core/listener/WorkflowStatusListener.java | 2 +- .../conductor/core/listener/WorkflowStatusListenerStub.java | 2 +- .../conductor/core/metadata/MetadataMapperService.java | 2 +- .../conductor/core/operation/StartWorkflowOperation.java | 2 +- .../netflix/conductor/core/operation/WorkflowOperation.java | 2 +- .../conductor/core/reconciliation/WorkflowReconciler.java | 2 +- .../core/reconciliation/WorkflowRepairService.java | 2 +- .../conductor/core/reconciliation/WorkflowSweeper.java | 2 +- .../netflix/conductor/core/storage/DummyPayloadStorage.java | 2 +- .../src/main/java/com/netflix/conductor/core/sync/Lock.java | 2 +- .../netflix/conductor/core/sync/local/LocalOnlyLock.java | 2 +- .../core/sync/local/LocalOnlyLockConfiguration.java | 2 +- .../java/com/netflix/conductor/core/sync/noop/NoopLock.java | 2 +- .../com/netflix/conductor/core/utils/DateTimeUtils.java | 2 +- .../conductor/core/utils/ExternalPayloadStorageUtils.java | 2 +- .../java/com/netflix/conductor/core/utils/IDGenerator.java | 2 +- .../java/com/netflix/conductor/core/utils/JsonUtils.java | 2 +- .../com/netflix/conductor/core/utils/ParametersUtils.java | 2 +- .../java/com/netflix/conductor/core/utils/QueueUtils.java | 2 +- .../com/netflix/conductor/core/utils/SemaphoreUtil.java | 2 +- .../main/java/com/netflix/conductor/core/utils/Utils.java | 2 +- .../netflix/conductor/dao/ConcurrentExecutionLimitDAO.java | 2 +- .../java/com/netflix/conductor/dao/EventHandlerDAO.java | 2 +- .../main/java/com/netflix/conductor/dao/ExecutionDAO.java | 2 +- core/src/main/java/com/netflix/conductor/dao/IndexDAO.java | 2 +- .../main/java/com/netflix/conductor/dao/MetadataDAO.java | 2 +- .../main/java/com/netflix/conductor/dao/PollDataDAO.java | 2 +- core/src/main/java/com/netflix/conductor/dao/QueueDAO.java | 2 +- .../java/com/netflix/conductor/dao/RateLimitingDAO.java | 2 +- .../main/java/com/netflix/conductor/metrics/Monitors.java | 2 +- .../java/com/netflix/conductor/metrics/WorkflowMonitor.java | 2 +- .../main/java/com/netflix/conductor/model/TaskModel.java | 2 +- .../java/com/netflix/conductor/model/WorkflowModel.java | 2 +- .../java/com/netflix/conductor/service/AdminService.java | 2 +- .../com/netflix/conductor/service/AdminServiceImpl.java | 2 +- .../java/com/netflix/conductor/service/EventService.java | 2 +- .../com/netflix/conductor/service/EventServiceImpl.java | 2 +- .../com/netflix/conductor/service/ExecutionLockService.java | 2 +- .../com/netflix/conductor/service/ExecutionService.java | 2 +- .../java/com/netflix/conductor/service/MetadataService.java | 2 +- .../com/netflix/conductor/service/MetadataServiceImpl.java | 2 +- .../java/com/netflix/conductor/service/TaskService.java | 2 +- .../java/com/netflix/conductor/service/TaskServiceImpl.java | 2 +- .../com/netflix/conductor/service/WorkflowBulkService.java | 2 +- .../netflix/conductor/service/WorkflowBulkServiceImpl.java | 2 +- .../java/com/netflix/conductor/service/WorkflowService.java | 2 +- .../com/netflix/conductor/service/WorkflowServiceImpl.java | 2 +- .../com/netflix/conductor/service/WorkflowTestService.java | 2 +- .../netflix/conductor/validations/ValidationContext.java | 2 +- .../conductor/validations/WorkflowTaskTypeConstraint.java | 2 +- .../core/execution/AsyncSystemTaskExecutorTest.groovy | 2 +- .../conductor/core/execution/tasks/DoWhileSpec.groovy | 2 +- .../netflix/conductor/core/execution/tasks/EventSpec.groovy | 2 +- .../execution/tasks/IsolatedTaskQueueProducerSpec.groovy | 2 +- .../conductor/core/execution/tasks/StartWorkflowSpec.groovy | 2 +- .../core/operation/StartWorkflowOperationSpec.groovy | 2 +- .../groovy/com/netflix/conductor/model/TaskModelSpec.groovy | 2 +- .../com/netflix/conductor/model/WorkflowModelSpec.groovy | 2 +- core/src/test/java/com/netflix/conductor/TestUtils.java | 2 +- .../netflix/conductor/core/dal/ExecutionDAOFacadeTest.java | 2 +- .../netflix/conductor/core/events/MockObservableQueue.java | 2 +- .../netflix/conductor/core/events/MockQueueProvider.java | 2 +- .../conductor/core/events/TestDefaultEventProcessor.java | 2 +- .../com/netflix/conductor/core/events/TestScriptEval.java | 2 +- .../conductor/core/events/TestSimpleActionProcessor.java | 2 +- .../conductor/core/execution/TestDeciderOutcomes.java | 2 +- .../conductor/core/execution/TestDeciderService.java | 2 +- .../netflix/conductor/core/execution/TestWorkflowDef.java | 2 +- .../conductor/core/execution/TestWorkflowExecutor.java | 2 +- .../conductor/core/execution/WorkflowSystemTaskStub.java | 2 +- .../core/execution/mapper/DecisionTaskMapperTest.java | 2 +- .../core/execution/mapper/DoWhileTaskMapperTest.java | 2 +- .../core/execution/mapper/DynamicTaskMapperTest.java | 2 +- .../core/execution/mapper/EventTaskMapperTest.java | 2 +- .../execution/mapper/ForkJoinDynamicTaskMapperTest.java | 2 +- .../core/execution/mapper/ForkJoinTaskMapperTest.java | 2 +- .../conductor/core/execution/mapper/HTTPTaskMapperTest.java | 2 +- .../core/execution/mapper/HumanTaskMapperTest.java | 2 +- .../core/execution/mapper/InlineTaskMapperTest.java | 2 +- .../conductor/core/execution/mapper/JoinTaskMapperTest.java | 2 +- .../execution/mapper/JsonJQTransformTaskMapperTest.java | 2 +- .../core/execution/mapper/KafkaPublishTaskMapperTest.java | 2 +- .../core/execution/mapper/LambdaTaskMapperTest.java | 2 +- .../conductor/core/execution/mapper/NoopTaskMapperTest.java | 2 +- .../core/execution/mapper/SetVariableTaskMapperTest.java | 2 +- .../core/execution/mapper/SimpleTaskMapperTest.java | 2 +- .../core/execution/mapper/SubWorkflowTaskMapperTest.java | 2 +- .../core/execution/mapper/SwitchTaskMapperTest.java | 2 +- .../core/execution/mapper/TerminateTaskMapperTest.java | 2 +- .../core/execution/mapper/UserDefinedTaskMapperTest.java | 2 +- .../conductor/core/execution/mapper/WaitTaskMapperTest.java | 2 +- .../core/execution/tasks/EventQueueResolutionTest.java | 2 +- .../netflix/conductor/core/execution/tasks/InlineTest.java | 2 +- .../netflix/conductor/core/execution/tasks/TestLambda.java | 2 +- .../netflix/conductor/core/execution/tasks/TestNoop.java | 2 +- .../conductor/core/execution/tasks/TestSubWorkflow.java | 2 +- .../core/execution/tasks/TestSystemTaskWorker.java | 2 +- .../execution/tasks/TestSystemTaskWorkerCoordinator.java | 2 +- .../conductor/core/execution/tasks/TestTerminate.java | 2 +- .../conductor/core/metadata/MetadataMapperServiceTest.java | 2 +- .../core/reconciliation/TestWorkflowRepairService.java | 2 +- .../conductor/core/reconciliation/TestWorkflowSweeper.java | 2 +- .../conductor/core/storage/DummyPayloadStorageTest.java | 2 +- .../conductor/core/sync/local/LocalOnlyLockTest.java | 2 +- .../core/utils/ExternalPayloadStorageUtilsTest.java | 2 +- .../com/netflix/conductor/core/utils/JsonUtilsTest.java | 2 +- .../netflix/conductor/core/utils/ParametersUtilsTest.java | 2 +- .../com/netflix/conductor/core/utils/QueueUtilsTest.java | 2 +- .../com/netflix/conductor/core/utils/SemaphoreUtilTest.java | 2 +- .../java/com/netflix/conductor/dao/ExecutionDAOTest.java | 2 +- .../java/com/netflix/conductor/dao/PollDataDAOTest.java | 2 +- .../com/netflix/conductor/metrics/WorkflowMonitorTest.java | 2 +- .../com/netflix/conductor/service/EventServiceTest.java | 2 +- .../com/netflix/conductor/service/ExecutionServiceTest.java | 2 +- .../com/netflix/conductor/service/MetadataServiceTest.java | 2 +- .../java/com/netflix/conductor/service/TaskServiceTest.java | 2 +- .../netflix/conductor/service/WorkflowBulkServiceTest.java | 2 +- .../com/netflix/conductor/service/WorkflowServiceTest.java | 2 +- .../conductor/validations/WorkflowDefConstraintTest.java | 2 +- .../validations/WorkflowTaskTypeConstraintTest.java | 2 +- .../conductor/es7/config/ElasticSearchConditions.java | 2 +- .../conductor/es7/config/ElasticSearchProperties.java | 2 +- .../conductor/es7/config/ElasticSearchV7Configuration.java | 2 +- .../conductor/es7/dao/index/BulkRequestBuilderWrapper.java | 2 +- .../netflix/conductor/es7/dao/index/BulkRequestWrapper.java | 2 +- .../conductor/es7/dao/index/ElasticSearchBaseDAO.java | 2 +- .../conductor/es7/dao/index/ElasticSearchRestDAOV7.java | 2 +- .../netflix/conductor/es7/dao/query/parser/Expression.java | 2 +- .../conductor/es7/dao/query/parser/FilterProvider.java | 2 +- .../conductor/es7/dao/query/parser/GroupedExpression.java | 2 +- .../netflix/conductor/es7/dao/query/parser/NameValue.java | 2 +- .../es7/dao/query/parser/internal/AbstractNode.java | 2 +- .../conductor/es7/dao/query/parser/internal/BooleanOp.java | 2 +- .../es7/dao/query/parser/internal/ComparisonOp.java | 2 +- .../conductor/es7/dao/query/parser/internal/ConstValue.java | 2 +- .../query/parser/internal/FunctionThrowingException.java | 2 +- .../conductor/es7/dao/query/parser/internal/ListConst.java | 2 +- .../conductor/es7/dao/query/parser/internal/Name.java | 2 +- .../es7/dao/query/parser/internal/ParserException.java | 2 +- .../conductor/es7/dao/query/parser/internal/Range.java | 2 +- .../es7/dao/index/ElasticSearchRestDaoBaseTest.java | 2 +- .../netflix/conductor/es7/dao/index/ElasticSearchTest.java | 2 +- .../es7/dao/index/TestBulkRequestBuilderWrapper.java | 2 +- .../conductor/es7/dao/index/TestElasticSearchRestDAOV7.java | 2 +- .../es7/dao/index/TestElasticSearchRestDAOV7Batch.java | 2 +- .../conductor/es7/dao/query/parser/TestExpression.java | 2 +- .../es7/dao/query/parser/TestGroupedExpression.java | 2 +- .../es7/dao/query/parser/internal/AbstractParserTest.java | 2 +- .../es7/dao/query/parser/internal/TestBooleanOp.java | 2 +- .../es7/dao/query/parser/internal/TestComparisonOp.java | 2 +- .../es7/dao/query/parser/internal/TestConstValue.java | 2 +- .../conductor/es7/dao/query/parser/internal/TestName.java | 2 +- .../java/com/netflix/conductor/es7/utils/TestUtils.java | 2 +- .../java/com/netflix/conductor/client/grpc/ClientBase.java | 2 +- .../java/com/netflix/conductor/client/grpc/EventClient.java | 2 +- .../com/netflix/conductor/client/grpc/MetadataClient.java | 2 +- .../java/com/netflix/conductor/client/grpc/TaskClient.java | 2 +- .../com/netflix/conductor/client/grpc/WorkflowClient.java | 2 +- .../com/netflix/conductor/client/grpc/EventClientTest.java | 2 +- .../com/netflix/conductor/client/grpc/TaskClientTest.java | 2 +- .../netflix/conductor/client/grpc/WorkflowClientTest.java | 2 +- .../java/com/netflix/conductor/grpc/server/GRPCServer.java | 2 +- .../netflix/conductor/grpc/server/GRPCServerProperties.java | 2 +- .../netflix/conductor/grpc/server/GrpcConfiguration.java | 2 +- .../conductor/grpc/server/service/EventServiceImpl.java | 2 +- .../netflix/conductor/grpc/server/service/GRPCHelper.java | 2 +- .../conductor/grpc/server/service/HealthServiceImpl.java | 2 +- .../conductor/grpc/server/service/MetadataServiceImpl.java | 2 +- .../conductor/grpc/server/service/TaskServiceImpl.java | 2 +- .../conductor/grpc/server/service/WorkflowServiceImpl.java | 2 +- .../grpc/server/service/HealthServiceImplTest.java | 2 +- .../conductor/grpc/server/service/TaskServiceImplTest.java | 2 +- .../grpc/server/service/WorkflowServiceImplTest.java | 2 +- .../java/com/netflix/conductor/tasks/http/HttpTask.java | 2 +- .../tasks/http/providers/DefaultRestTemplateProvider.java | 2 +- .../tasks/http/providers/RestTemplateProvider.java | 2 +- .../java/com/netflix/conductor/tasks/http/HttpTaskTest.java | 2 +- .../http/providers/DefaultRestTemplateProviderTest.java | 2 +- .../com/netflix/conductor/sdk/example/shipment/Order.java | 2 +- .../netflix/conductor/sdk/example/shipment/Shipment.java | 2 +- .../conductor/sdk/example/shipment/ShipmentState.java | 2 +- .../conductor/sdk/example/shipment/ShipmentWorkers.java | 2 +- .../conductor/sdk/example/shipment/ShipmentWorkflow.java | 2 +- .../com/netflix/conductor/sdk/example/shipment/User.java | 2 +- .../conductor/sdk/healthcheck/HealthCheckClient.java | 2 +- .../netflix/conductor/sdk/testing/LocalServerRunner.java | 2 +- .../netflix/conductor/sdk/testing/WorkflowTestRunner.java | 2 +- .../conductor/sdk/workflow/def/ConductorWorkflow.java | 2 +- .../netflix/conductor/sdk/workflow/def/ValidationError.java | 2 +- .../netflix/conductor/sdk/workflow/def/WorkflowBuilder.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/DoWhile.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/Dynamic.java | 2 +- .../conductor/sdk/workflow/def/tasks/DynamicFork.java | 2 +- .../conductor/sdk/workflow/def/tasks/DynamicForkInput.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Event.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Http.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/JQ.java | 2 +- .../conductor/sdk/workflow/def/tasks/Javascript.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Join.java | 2 +- .../conductor/sdk/workflow/def/tasks/SetVariable.java | 2 +- .../conductor/sdk/workflow/def/tasks/SimpleTask.java | 2 +- .../conductor/sdk/workflow/def/tasks/SubWorkflow.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/Switch.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Task.java | 2 +- .../conductor/sdk/workflow/def/tasks/TaskRegistry.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/Terminate.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Wait.java | 2 +- .../conductor/sdk/workflow/executor/WorkflowExecutor.java | 2 +- .../sdk/workflow/executor/task/AnnotatedWorker.java | 2 +- .../sdk/workflow/executor/task/AnnotatedWorkerExecutor.java | 2 +- .../sdk/workflow/executor/task/DynamicForkWorker.java | 2 +- .../sdk/workflow/executor/task/NonRetryableException.java | 2 +- .../conductor/sdk/workflow/executor/task/TaskContext.java | 2 +- .../sdk/workflow/executor/task/WorkerConfiguration.java | 2 +- .../com/netflix/conductor/sdk/workflow/task/InputParam.java | 2 +- .../netflix/conductor/sdk/workflow/task/OutputParam.java | 2 +- .../com/netflix/conductor/sdk/workflow/task/WorkerTask.java | 2 +- .../conductor/sdk/workflow/utils/InputOutputGetter.java | 2 +- .../netflix/conductor/sdk/workflow/utils/MapBuilder.java | 2 +- .../conductor/sdk/workflow/utils/ObjectMapperProvider.java | 2 +- .../conductor/sdk/workflow/def/TaskConversionsTests.java | 2 +- .../conductor/sdk/workflow/def/WorkflowCreationTests.java | 2 +- .../conductor/sdk/workflow/def/WorkflowDefTaskTests.java | 2 +- .../netflix/conductor/sdk/workflow/def/WorkflowState.java | 2 +- .../sdk/workflow/executor/task/AnnotatedWorkerTests.java | 2 +- .../sdk/workflow/executor/task/TestWorkerConfig.java | 2 +- .../netflix/conductor/sdk/workflow/testing/Task1Input.java | 2 +- .../conductor/sdk/workflow/testing/TestWorkflowInput.java | 2 +- .../sdk/workflow/testing/WorkflowTestFrameworkTests.java | 2 +- .../com/netflix/conductor/tasks/json/JsonJqTransform.java | 2 +- .../netflix/conductor/tasks/json/JsonJqTransformTest.java | 2 +- .../contribs/tasks/kafka/KafkaProducerManager.java | 2 +- .../conductor/contribs/tasks/kafka/KafkaPublishTask.java | 2 +- .../core/execution/mapper/KafkaPublishTaskMapper.java | 2 +- .../contribs/tasks/kafka/KafkaProducerManagerTest.java | 2 +- .../contribs/tasks/kafka/KafkaPublishTaskTest.java | 2 +- .../core/execution/mapper/KafkaPublishTaskMapperTest.java | 2 +- licenseheader.txt | 2 +- .../contribs/metrics/LoggingMetricsConfiguration.java | 2 +- .../contribs/metrics/MetricsRegistryConfiguration.java | 2 +- .../contribs/metrics/PrometheusMetricsConfiguration.java | 2 +- .../contribs/metrics/LoggingMetricsConfigurationTest.java | 2 +- .../metrics/PrometheusMetricsConfigurationTest.java | 2 +- .../netflix/conductor/mysql/config/MySQLConfiguration.java | 2 +- .../com/netflix/conductor/mysql/config/MySQLProperties.java | 2 +- .../java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java | 2 +- .../com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java | 2 +- .../com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java | 2 +- .../java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java | 2 +- .../com/netflix/conductor/mysql/util/ExecuteFunction.java | 2 +- .../java/com/netflix/conductor/mysql/util/LazyToString.java | 2 +- .../main/java/com/netflix/conductor/mysql/util/Query.java | 2 +- .../com/netflix/conductor/mysql/util/QueryFunction.java | 2 +- .../com/netflix/conductor/mysql/util/ResultSetHandler.java | 2 +- .../netflix/conductor/mysql/util/TransactionalFunction.java | 2 +- .../netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java | 2 +- .../netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java | 2 +- .../com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java | 2 +- .../test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java | 2 +- .../conductor/contribs/queue/stan/NATSAbstractQueue.java | 2 +- .../conductor/contribs/queue/stan/NATSObservableQueue.java | 2 +- .../contribs/queue/stan/NATSStreamObservableQueue.java | 2 +- .../contribs/queue/stan/config/NATSConfiguration.java | 2 +- .../contribs/queue/stan/config/NATSEventQueueProvider.java | 2 +- .../contribs/queue/stan/config/NATSStreamConfiguration.java | 2 +- .../queue/stan/config/NATSStreamEventQueueProvider.java | 2 +- .../contribs/queue/stan/config/NATSStreamProperties.java | 2 +- .../contribs/queue/nats/JetStreamObservableQueue.java | 2 +- .../netflix/conductor/contribs/queue/nats/JsmMessage.java | 2 +- .../conductor/contribs/queue/nats/NATSAbstractQueue.java | 2 +- .../conductor/contribs/queue/nats/NATSObservableQueue.java | 2 +- .../conductor/contribs/queue/nats/NatsException.java | 2 +- .../contribs/queue/nats/config/JetStreamConfiguration.java | 2 +- .../queue/nats/config/JetStreamEventQueueProvider.java | 2 +- .../contribs/queue/nats/config/JetStreamProperties.java | 2 +- .../contribs/queue/nats/config/NATSConfiguration.java | 2 +- .../contribs/queue/nats/config/NATSEventQueueProvider.java | 2 +- .../postgres/config/PostgresPayloadConfiguration.java | 2 +- .../postgres/config/PostgresPayloadProperties.java | 2 +- .../controller/ExternalPostgresPayloadResource.java | 2 +- .../conductor/postgres/storage/PostgresPayloadStorage.java | 2 +- .../controller/ExternalPostgresPayloadResourceTest.java | 2 +- .../postgres/storage/PostgresPayloadStorageTest.java | 2 +- .../conductor/postgres/storage/PostgresPayloadTestUtil.java | 2 +- .../conductor/postgres/config/PostgresConfiguration.java | 2 +- .../conductor/postgres/config/PostgresProperties.java | 2 +- .../com/netflix/conductor/postgres/dao/PostgresBaseDAO.java | 2 +- .../conductor/postgres/dao/PostgresExecutionDAO.java | 2 +- .../netflix/conductor/postgres/dao/PostgresIndexDAO.java | 2 +- .../netflix/conductor/postgres/dao/PostgresMetadataDAO.java | 2 +- .../netflix/conductor/postgres/dao/PostgresQueueDAO.java | 2 +- .../netflix/conductor/postgres/util/ExecuteFunction.java | 2 +- .../com/netflix/conductor/postgres/util/ExecutorsUtil.java | 2 +- .../com/netflix/conductor/postgres/util/LazyToString.java | 2 +- .../conductor/postgres/util/PostgresIndexQueryBuilder.java | 2 +- .../java/com/netflix/conductor/postgres/util/Query.java | 2 +- .../com/netflix/conductor/postgres/util/QueryFunction.java | 2 +- .../netflix/conductor/postgres/util/ResultSetHandler.java | 2 +- .../conductor/postgres/util/TransactionalFunction.java | 2 +- .../conductor/postgres/dao/PostgresExecutionDAOTest.java | 2 +- .../conductor/postgres/dao/PostgresIndexDAOTest.java | 2 +- .../conductor/postgres/dao/PostgresMetadataDAOTest.java | 2 +- .../conductor/postgres/dao/PostgresQueueDAOTest.java | 2 +- .../conductor/postgres/performance/PerformanceTest.java | 2 +- .../postgres/util/PostgresIndexQueryBuilderTest.java | 2 +- .../integration/grpc/postgres/PostgresGrpcEndToEndTest.java | 2 +- .../redis/limit/RedisConcurrentExecutionLimitDAO.java | 2 +- .../config/RedisConcurrentExecutionLimitConfiguration.java | 2 +- .../config/RedisConcurrentExecutionLimitProperties.java | 2 +- .../redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy | 2 +- .../conductor/redislock/config/RedisLockConfiguration.java | 2 +- .../conductor/redislock/config/RedisLockProperties.java | 2 +- .../com/netflix/conductor/redislock/lock/RedisLock.java | 2 +- .../com/netflix/conductor/redis/lock/RedisLockTest.java | 2 +- .../netflix/conductor/redis/config/AnyRedisCondition.java | 2 +- .../redis/config/DynomiteClusterConfiguration.java | 2 +- .../conductor/redis/config/InMemoryRedisConfiguration.java | 2 +- .../conductor/redis/config/JedisCommandsConfigurer.java | 2 +- .../conductor/redis/config/RedisClusterConfiguration.java | 2 +- .../conductor/redis/config/RedisCommonConfiguration.java | 2 +- .../com/netflix/conductor/redis/config/RedisProperties.java | 2 +- .../conductor/redis/config/RedisSentinelConfiguration.java | 2 +- .../redis/config/RedisStandaloneConfiguration.java | 2 +- .../java/com/netflix/conductor/redis/dao/BaseDynoDAO.java | 2 +- .../java/com/netflix/conductor/redis/dao/DynoQueueDAO.java | 2 +- .../netflix/conductor/redis/dao/RedisEventHandlerDAO.java | 2 +- .../com/netflix/conductor/redis/dao/RedisExecutionDAO.java | 2 +- .../com/netflix/conductor/redis/dao/RedisMetadataDAO.java | 2 +- .../com/netflix/conductor/redis/dao/RedisPollDataDAO.java | 2 +- .../netflix/conductor/redis/dao/RedisRateLimitingDAO.java | 2 +- .../redis/dynoqueue/ConfigurationHostSupplier.java | 2 +- .../conductor/redis/dynoqueue/LocalhostHostSupplier.java | 2 +- .../dynoqueue/RedisQueuesShardingStrategyProvider.java | 2 +- .../com/netflix/conductor/redis/jedis/JedisCluster.java | 2 +- .../java/com/netflix/conductor/redis/jedis/JedisMock.java | 2 +- .../java/com/netflix/conductor/redis/jedis/JedisProxy.java | 2 +- .../com/netflix/conductor/redis/jedis/JedisSentinel.java | 2 +- .../com/netflix/conductor/redis/jedis/JedisStandalone.java | 2 +- .../utils/RedisQueuesShardingStrategyProviderTest.java | 2 +- .../com/netflix/conductor/redis/dao/BaseDynoDAOTest.java | 2 +- .../com/netflix/conductor/redis/dao/DynoQueueDAOTest.java | 2 +- .../conductor/redis/dao/RedisEventHandlerDAOTest.java | 2 +- .../netflix/conductor/redis/dao/RedisExecutionDAOTest.java | 2 +- .../netflix/conductor/redis/dao/RedisMetadataDAOTest.java | 2 +- .../netflix/conductor/redis/dao/RedisPollDataDAOTest.java | 2 +- .../netflix/conductor/redis/dao/RedisRateLimitDAOTest.java | 2 +- .../redis/jedis/ConfigurationHostSupplierTest.java | 2 +- .../com/netflix/conductor/redis/jedis/JedisClusterTest.java | 2 +- .../netflix/conductor/redis/jedis/JedisSentinelTest.java | 2 +- .../conductor/rest/config/RequestMappingConstants.java | 2 +- .../netflix/conductor/rest/config/RestConfiguration.java | 2 +- .../netflix/conductor/rest/controllers/AdminResource.java | 2 +- .../rest/controllers/ApplicationExceptionMapper.java | 2 +- .../netflix/conductor/rest/controllers/EventResource.java | 2 +- .../conductor/rest/controllers/HealthCheckResource.java | 2 +- .../conductor/rest/controllers/MetadataResource.java | 2 +- .../conductor/rest/controllers/QueueAdminResource.java | 2 +- .../netflix/conductor/rest/controllers/TaskResource.java | 2 +- .../rest/controllers/ValidationExceptionMapper.java | 2 +- .../conductor/rest/controllers/WorkflowBulkResource.java | 2 +- .../conductor/rest/controllers/WorkflowResource.java | 2 +- .../conductor/rest/startup/KitchenSinkInitializer.java | 2 +- .../conductor/rest/controllers/AdminResourceTest.java | 2 +- .../conductor/rest/controllers/EventResourceTest.java | 2 +- .../conductor/rest/controllers/MetadataResourceTest.java | 2 +- .../conductor/rest/controllers/TaskResourceTest.java | 2 +- .../conductor/rest/controllers/WorkflowResourceTest.java | 2 +- server/src/main/java/com/netflix/conductor/Conductor.java | 2 +- .../conductor/common/config/ConductorObjectMapperTest.java | 2 +- .../test/base/AbstractResiliencySpecification.groovy | 2 +- .../conductor/test/base/AbstractSpecification.groovy | 2 +- .../conductor/test/integration/DecisionTaskSpec.groovy | 2 +- .../netflix/conductor/test/integration/DoWhileSpec.groovy | 2 +- .../conductor/test/integration/DynamicForkJoinSpec.groovy | 2 +- .../netflix/conductor/test/integration/EventTaskSpec.groovy | 2 +- .../conductor/test/integration/ExclusiveJoinSpec.groovy | 2 +- .../test/integration/ExternalPayloadStorageSpec.groovy | 2 +- .../conductor/test/integration/FailureWorkflowSpec.groovy | 2 +- .../netflix/conductor/test/integration/ForkJoinSpec.groovy | 2 +- .../HierarchicalForkJoinSubworkflowRerunSpec.groovy | 2 +- .../HierarchicalForkJoinSubworkflowRestartSpec.groovy | 2 +- .../HierarchicalForkJoinSubworkflowRetrySpec.groovy | 2 +- .../conductor/test/integration/JsonJQTransformSpec.groovy | 2 +- .../test/integration/LambdaAndTerminateTaskSpec.groovy | 2 +- .../test/integration/NestedForkJoinSubWorkflowSpec.groovy | 2 +- .../conductor/test/integration/SetVariableTaskSpec.groovy | 2 +- .../conductor/test/integration/SimpleWorkflowSpec.groovy | 2 +- .../conductor/test/integration/StartWorkflowSpec.groovy | 2 +- .../conductor/test/integration/SubWorkflowRerunSpec.groovy | 2 +- .../test/integration/SubWorkflowRestartSpec.groovy | 2 +- .../conductor/test/integration/SubWorkflowRetrySpec.groovy | 2 +- .../conductor/test/integration/SubWorkflowSpec.groovy | 2 +- .../conductor/test/integration/SwitchTaskSpec.groovy | 2 +- .../conductor/test/integration/SystemTaskSpec.groovy | 2 +- .../test/integration/TaskLimitsWorkflowSpec.groovy | 2 +- .../conductor/test/integration/TestWorkflowSpec.groovy | 2 +- .../netflix/conductor/test/integration/WaitTaskSpec.groovy | 2 +- .../integration/WorkflowAndTaskConfigurationSpec.groovy | 2 +- .../conductor/test/resiliency/QueueResiliencySpec.groovy | 2 +- .../conductor/test/resiliency/TaskResiliencySpec.groovy | 2 +- .../com/netflix/conductor/test/util/WorkflowTestUtil.groovy | 2 +- .../test/java/com/netflix/conductor/ConductorTestApp.java | 2 +- .../conductor/test/integration/AbstractEndToEndTest.java | 2 +- .../test/integration/grpc/AbstractGrpcEndToEndTest.java | 2 +- .../conductor/test/integration/grpc/GrpcEndToEndTest.java | 2 +- .../test/integration/http/AbstractHttpEndToEndTest.java | 2 +- .../conductor/test/integration/http/HttpEndToEndTest.java | 2 +- .../conductor/test/utils/MockExternalPayloadStorage.java | 2 +- .../java/com/netflix/conductor/test/utils/UserTask.java | 2 +- .../test/java/com/netflix/conductor/ConductorTestApp.java | 2 +- .../common/config/TestObjectMapperConfiguration.java | 2 +- .../conductor/test/integration/AbstractEndToEndTest.java | 2 +- .../test/integration/grpc/AbstractGrpcEndToEndTest.java | 2 +- .../archive/ArchivingWithTTLWorkflowStatusListener.java | 2 +- .../archive/ArchivingWorkflowListenerConfiguration.java | 2 +- .../archive/ArchivingWorkflowListenerProperties.java | 2 +- .../listener/archive/ArchivingWorkflowStatusListener.java | 2 +- .../conductorqueue/ConductorQueueStatusPublisher.java | 2 +- .../ConductorQueueStatusPublisherConfiguration.java | 2 +- .../ConductorQueueStatusPublisherProperties.java | 2 +- .../listener/ArchivingWorkflowStatusListenerTest.java | 2 +- .../listener/WorkflowStatusPublisherIntegrationTest.java | 2 +- 669 files changed, 671 insertions(+), 671 deletions(-) diff --git a/README.md b/README.md index c9525ae62..83b2cbf96 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ![Conductor](docs/docs/img/logo.png) [![Github release](https://img.shields.io/github/v/release/Netflix/conductor.svg)](https://GitHub.com/Netflix/conductor/releases) -[![License](https://img.shields.io/github/license/Netflix/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) +[![License](https://img.shields.io/github/license/conductor-oss/conductor.svg)](http://www.apache.org/licenses/LICENSE-2.0) -Conductor is a platform originally created at Netflix to orchestrate microservices and events. +Conductor is a platform _originally_ created at Netflix to orchestrate microservices and events. Conductor OSS is maintained by the team of developers at [Orkes](https://orkes.io/) along with the members of the open source community. ## Conductor OSS @@ -13,7 +13,7 @@ This is the new home for the Conductor open source going forward (previously hos > Going forward, all the bug fixes, feature requests and security patches will be applied and released from this repository. -The last published version of Netflix Condutor will be **3.15.0** which we will continue to support. +The last published version of Netflix Conductor will be **3.15.0** which we will continue to support. If you would like to participate in the roadmap and development, [please reach out](https://forms.gle/P2i1xHrxPQLrjzTB7). diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java index c308d1593..14609a3d5 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java index 15d33f09d..305359979 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java index b23b64fe0..4b959530f 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java index ac0a08dae..f5601733c 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java index 51bcc4082..6806fcad2 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java index 736cb5f42..4ca45975b 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java index 36bd7a6cf..b4ba34d84 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java index fc9aa9892..6cebcf10b 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java index 4684d16a3..410847396 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java index c6cfdc273..e168bcac8 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java index 00c683527..1be5e6337 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java index b881e4cc1..f99f464d0 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java index 9a16cae64..84bf32b65 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java index 7b9f60e1f..1776c381d 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/annotations-processor/src/example/java/com/example/Example.java b/annotations-processor/src/example/java/com/example/Example.java index b3c7befe8..f71f09c28 100644 --- a/annotations-processor/src/example/java/com/example/Example.java +++ b/annotations-processor/src/example/java/com/example/Example.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java index bc92d901f..f4c7aef1a 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java index 3944bafb1..5916f7f5e 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java index 9dfaf2883..45fe055ad 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java index 1bd543a60..58624f874 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java index e694918c3..836bb4b23 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java index fb411fc4f..0819555e9 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java index fbfa8e72c..83999171c 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java index ed7eaae24..a04b7803d 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java index 5bad20a2f..75c007174 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java index 921594391..e56fdf073 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java index fe642fdec..88eed8db3 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java index d57228773..3cb8938bc 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java index c6958bdd9..1e04438f9 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java index 2363ed365..d21129858 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java index c6d04e172..ac0567c7b 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java b/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java index 0fe7a243b..7e22160d6 100644 --- a/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java +++ b/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java index 1514a3ed8..99873a8aa 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java index 25ab478c8..c238a9358 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java index d66e4aa43..c98b55e16 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java index 5b8e6b3dc..c9b95f6df 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java index 94a515f72..6630842db 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java index 838ab4088..75492872a 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java index fb3065895..f3de6fd35 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java index 4bd9eb9ba..8570e538a 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java index 2d0b45e21..3b76cd016 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java index 95a1771b7..71f2fde6f 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java index ab7be3118..5a4b93d4e 100644 --- a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java +++ b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java index 7c0d69aa5..c336b4214 100644 --- a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java +++ b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java index 476ad1dad..e73935d5d 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java index cb5450923..bd52006c6 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java index 4ad51fb33..7d5d58756 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java index b5fb30aee..f47857a13 100644 --- a/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java +++ b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java index 14c3c022b..4cd97652c 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java index 28d3eee97..69c481d0d 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java index fabfd3c55..cad60b79d 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java index 2facedbd0..c6eec0d99 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java index bd0f178f5..25b2aec52 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java index b327c18fb..a954cf004 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java index 4fa72a2ea..540fa02a1 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java index ce564af0a..6b424981f 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java index 90ff3ed4a..07c649594 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java index 235dd44f4..4e0d4f040 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java index 473c23132..284db18e2 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java index 68fe3b242..8f2b8cdb3 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy index 214f3722d..ae4b1ec39 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy index 14f612967..01c57923c 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy index fd8afacc5..d81d2f317 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy index a5393210b..8f39b69a8 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy index f826a3620..ccb1d1713 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java index 55c82e576..e5c46604a 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java index a730f24eb..f4c827efc 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java index 94d69bbde..10bf1458d 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java index 6d44e779c..a89f6b90e 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java b/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java index 772c04dc0..396350d03 100644 --- a/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java +++ b/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java b/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java index d28bf0c8d..e28930b60 100644 --- a/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java +++ b/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java b/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java index 334501fac..b1e9472ed 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java b/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java index 3b011c93d..5ce3d119f 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java index 3709f2aa5..1208427a6 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java b/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java index 6c3029fa1..27551029b 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java +++ b/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Netflix, Inc. + * Copyright 2018 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java b/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java index f15cf3bab..1b40e2fd6 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java +++ b/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java b/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java index 443b85481..b3736a2eb 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java +++ b/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java b/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java index 5f3c79c00..a72ed8806 100644 --- a/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java +++ b/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java b/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java index a27da869d..fea6cb7a5 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java +++ b/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java b/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java index 38749c1c5..5bfe06b1e 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java +++ b/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/EventClient.java b/client/src/main/java/com/netflix/conductor/client/http/EventClient.java index 660c2b44a..0d0ddddc7 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/EventClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/EventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java b/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java index 8db927b8c..915ca55d0 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java b/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java index 0b05745f0..bd2f791df 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java +++ b/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java b/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java index d624ef0f6..1654fcc83 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java b/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java index 6bfa686bb..b8c5083b0 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java b/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java index 7b9bb0ca3..9439838ee 100644 --- a/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java +++ b/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/worker/Worker.java b/client/src/main/java/com/netflix/conductor/client/worker/Worker.java index 9f07b151c..b3a213078 100644 --- a/client/src/main/java/com/netflix/conductor/client/worker/Worker.java +++ b/client/src/main/java/com/netflix/conductor/client/worker/Worker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy index 5c4a5208d..abf5719d7 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy index f4f32a767..bb6599271 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy index d82acc509..4d3efcddb 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy index bf21d8107..db9daf5b7 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy index 08ebfee83..73b81884f 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java b/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java index 38735b120..c7fa71062 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java b/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java index 5bbe8ba24..a9375d1c1 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java b/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java index d86ae76f1..c68c2ff48 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java b/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java index 87b6f40e4..365ebad9b 100644 --- a/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java +++ b/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/sample/Main.java b/client/src/test/java/com/netflix/conductor/client/sample/Main.java index 6fbbb00d1..b91cc4582 100644 --- a/client/src/test/java/com/netflix/conductor/client/sample/Main.java +++ b/client/src/test/java/com/netflix/conductor/client/sample/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java b/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java index cc2cbda60..927e1b9fc 100644 --- a/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java +++ b/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java b/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java index 69703816d..6e1689c1a 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java index cb48576a6..d8db7646f 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java index df09964d8..5a1ccb693 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java b/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java index 4b51a2df2..4f1cf70d4 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java b/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java index 5994d8c1c..2eed2d85c 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java b/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java index 62720e92f..c084f4dc2 100644 --- a/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java +++ b/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java index a2688688d..0ddbacbbb 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java index eb78c531b..999028732 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java index a281edb34..326aa5938 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java index fefce75ba..8828f4d0e 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java index 43398df79..e6d3ce1d9 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java index 6f3015e6a..cdb270e6a 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java index b010b4bb3..434b581c0 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java index f9dbc4c79..758ab88e7 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java index a498dca65..d360a9536 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java b/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java index 29bb5e11d..56eca4467 100644 --- a/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java +++ b/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java b/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java index 01f229480..c83dc925d 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java index d638d2d32..87e33f79a 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java b/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java index 26736f77e..9065b1034 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java index d6a2065e6..3b1f4c36d 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java index 24084f2de..947d76146 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java index b058e2cd4..534721386 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java index f51016340..095bd00e9 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java index 658079e0a..45b9afed2 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java index 256e1da6f..e044af1f0 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java index f31b6481b..1414f9e9a 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java index 235a0ac91..de22c0340 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java index d95354ef5..3f2fe73cc 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java index f11530dc7..b3f57cf5d 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java index 67c1b86a7..4c7850ac7 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java index 8540794a6..fbbef2153 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java index e44edca62..1974046f6 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java index 9cb934b64..fa108ce40 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java index 6d0676026..bc591edda 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java index bf22c0265..e00fc00fb 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java index 6395596f8..344249ab8 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java b/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java index b0f5b38e6..112333489 100644 --- a/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java +++ b/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java b/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java index 5c3071613..09c479a18 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java +++ b/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java b/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java index 72be415a9..73fe30bec 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java +++ b/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java b/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java index b9e0baa6c..45c3125ec 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java index b84c99775..d988c8ec1 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java +++ b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java b/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java index 7fec015fc..82498a0fa 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java b/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java index e5007348b..eab019f64 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java b/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java index 3f2eea503..0d1cb8e67 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java b/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java index 202f1f31c..9120d6235 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java b/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java index abe74c734..c860e487d 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java index 60502402f..12e64bffb 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java b/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java index 5e83bd73e..65191d4ff 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java b/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java index 5ed6256e1..e76fa2260 100644 --- a/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java +++ b/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java b/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java index 48a53e066..ada55929e 100644 --- a/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java +++ b/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java index 014a118dd..66e07e565 100644 --- a/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java +++ b/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java index 36cf22c50..1a6681f04 100644 --- a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java +++ b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java b/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java index 4c8ec4e6c..de72cad0a 100644 --- a/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java +++ b/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java index 7e5108b05..0991b9033 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java index a49fa2daf..9ae897ae1 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java index e392f65b5..a6c9941ff 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java b/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java index d29f1744e..1ce01bbc6 100644 --- a/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java +++ b/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java b/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java index 79fae89d9..730f0c222 100644 --- a/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java +++ b/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java index bda8e0ddb..7d1b50ae3 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java index 2ed545418..b37e6af2c 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java index 0d9ea921e..3192f62fb 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/Audit.java b/core/src/main/java/com/netflix/conductor/annotations/Audit.java index 6f1d47199..dc779d24a 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/Audit.java +++ b/core/src/main/java/com/netflix/conductor/annotations/Audit.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/Trace.java b/core/src/main/java/com/netflix/conductor/annotations/Trace.java index 61da42cc4..d750c47e1 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/Trace.java +++ b/core/src/main/java/com/netflix/conductor/annotations/Trace.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java b/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java index 492931128..7a7bae16f 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java +++ b/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java b/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java index bfe1455fb..7dfd74dfc 100644 --- a/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java +++ b/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java b/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java index d870761c5..6575cf517 100644 --- a/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java +++ b/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java b/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java index a09403d5f..17a9d1a45 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java b/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java index c54b7a9de..07b4eaf0b 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java +++ b/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java b/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java index 364406e10..c24e4ef46 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java index 92255bc4d..d2ffbc71a 100644 --- a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java +++ b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java b/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java index 28c4fca72..3e5748c56 100644 --- a/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java +++ b/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java b/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java index 2b139636c..38b21b797 100644 --- a/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java +++ b/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java index 6b8139652..90da4b775 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java index fa7f1725b..2f45d6848 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java index 862287cac..e9f492347 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java +++ b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java index fc6a568f6..b31fa737b 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java index 8bd11f929..c291f27fd 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java index b4ab2e388..306f1c8c5 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java b/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java index dc46354b0..5543bb60b 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java index 27b81edf7..2c292160a 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java index 76e530101..c82fc2e88 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java index 649cc7b50..142290f3d 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java index f1c508803..22b2fceab 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java b/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java index b7d33961f..b97db4420 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java index ecdc75921..d29b1ada2 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java b/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java index 7c718ed59..54f33bd6e 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java b/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java index 4af05633f..318607dc3 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java b/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java index a89b7beed..cc8ab0fb5 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java b/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java index 366c4864f..e244e6733 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java b/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java index 87cc9d852..aeb3949bf 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java index 331a2b4ac..eb14ce93a 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java index c6bf486ba..c0985ce44 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java b/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java index 7eb8fb93d..86e83355b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java index 6070741a2..01e1699ab 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java index 88d01ef7f..c906a4ec4 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java index 8f2de942d..a4911d186 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java index f1fda6178..72a8e5ecd 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java index d3f494f3b..4254d7c76 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java index 66e6978d7..303a51ae2 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java index a669cc33c..f6e95852b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java index c65423a59..f498aa235 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java index dc648f015..48c1c03d7 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java index 8d9126df8..15f9ba36b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java index 7ac7f7d3d..089616efc 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java index 123e0aa76..a56cf8a6a 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java index 781dac871..4eda67abd 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java index ac95c5d22..56b4274a3 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java index 9e78154c5..6fb8e8ae9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java index c4f0eaeb0..ad025d6d1 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java index 280bf71d5..bb9af7761 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java index 0f39ae363..437a1252f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java index a7f904582..152e3f28b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java index 7881fb87f..b1d0ef5f9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java index 1dcf9ddb0..20f313e1a 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java index 4d965bc03..d78691b49 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java index a90964dac..466f8194f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java index 3503dbb29..4b4360004 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java index a80a9076e..7f9610275 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java index a34c4a0e4..1a96aba95 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java index 0491df06d..be4b2627c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java index b98288577..80bbd6686 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java index 6a3ca95d1..6b979c6db 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java index 0a96845f7..26f041c63 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java index 3dbc72eb9..e0dc0ea9c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java index 8c543ca46..a71e59f7d 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java index e2bf0ac0b..bb868ad40 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java index 7115dfd1d..fc5aeb149 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java index 9f31af750..78f88e782 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java index c4dfc4e31..677613152 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java index e53bd7dd4..67ac5f2a0 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java index 5a5ac33a8..7ffa15a68 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java index e0f7be9fa..3d032cfe3 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java index d75360e2b..a9fcac9f9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java index 4b9de40a2..34a538607 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java index 2af835cdd..79ed0e8f4 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java index 1096fdf50..8cfe160dc 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java index 4b0cc6f04..0d2a9c789 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java index 2d3d040a2..57d65f239 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java index 4486680e8..dffef6cb3 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java index 9b573f4af..066755d0f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java index b1a9ed988..1d57abd3b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java index 230ab82c2..c38fd87c5 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java index 903355aa6..39d7487e3 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java index b531d0001..4f898773f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java index 5b2d95870..d57085e84 100644 --- a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java +++ b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java index 0e9e2466b..bcb488c04 100644 --- a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java index 2fea7d065..3823e3b2a 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java index 413f5a69e..99a811f3b 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java index 1c0c4395a..586681313 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java index 36bb5e699..e4789ff7c 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java b/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java index 7a825adac..63aee4fc9 100644 --- a/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java +++ b/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java b/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java index e9281de64..3e3632aee 100644 --- a/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java +++ b/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java b/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java index c0b428cf9..963391478 100644 --- a/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java +++ b/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java index d5e8fe5db..972f28da8 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java index 49ca8a632..ca2e0d8ac 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java index cd4b46be7..01e1eb606 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java b/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java index a04a43710..4566860f0 100644 --- a/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java +++ b/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/Lock.java b/core/src/main/java/com/netflix/conductor/core/sync/Lock.java index cb8d5ccd4..02ccd48dd 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/Lock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/Lock.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java index 17cbee4fe..c2255d834 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java index 41a025406..ad90d327f 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java b/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java index 5d492da1e..55de2cfee 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java index ec7ce83f0..52cff75e8 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java index 7cbc80e9e..771baffc1 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java b/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java index 813d63aa9..488373173 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java index 38f70218c..a654d299c 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java index 15f8363f6..9a8b2d8a9 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java index bca477390..7dafb2617 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java b/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java index 793494bd9..65b208eea 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/Utils.java b/core/src/main/java/com/netflix/conductor/core/utils/Utils.java index e81b3cf58..b8be0b0e5 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/Utils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java b/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java index 853a72e63..d083d5545 100644 --- a/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java b/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java index 6c9dc47a9..b7eb1f3e0 100644 --- a/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java b/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java index 8e33cac29..2990afa8e 100644 --- a/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java b/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java index 14e53b2ab..bb51e8309 100644 --- a/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java b/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java index b7e39cf3a..6bb6034a2 100644 --- a/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java b/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java index d53d6660d..ce8ee0422 100644 --- a/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java b/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java index 70b5857c5..ba190e3ac 100644 --- a/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java b/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java index 6ef4b4859..6c2231d24 100644 --- a/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/metrics/Monitors.java b/core/src/main/java/com/netflix/conductor/metrics/Monitors.java index 862d3076d..946e1553c 100644 --- a/core/src/main/java/com/netflix/conductor/metrics/Monitors.java +++ b/core/src/main/java/com/netflix/conductor/metrics/Monitors.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java b/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java index 2f974ed68..422c3e0ca 100644 --- a/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java +++ b/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/model/TaskModel.java b/core/src/main/java/com/netflix/conductor/model/TaskModel.java index 617b013da..eebbf8cb3 100644 --- a/core/src/main/java/com/netflix/conductor/model/TaskModel.java +++ b/core/src/main/java/com/netflix/conductor/model/TaskModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java b/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java index ca505aa9c..995c5ee83 100644 --- a/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java +++ b/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/AdminService.java b/core/src/main/java/com/netflix/conductor/service/AdminService.java index 474743db4..2ebffc954 100644 --- a/core/src/main/java/com/netflix/conductor/service/AdminService.java +++ b/core/src/main/java/com/netflix/conductor/service/AdminService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java index bf470d4e8..88b980633 100644 --- a/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/EventService.java b/core/src/main/java/com/netflix/conductor/service/EventService.java index 397503cf4..36ababc5e 100644 --- a/core/src/main/java/com/netflix/conductor/service/EventService.java +++ b/core/src/main/java/com/netflix/conductor/service/EventService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java index b4b20bdf3..852a5bbb1 100644 --- a/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java b/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java index c45bba263..63a530fac 100644 --- a/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java +++ b/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java index a4e411b4e..fcb23b50b 100644 --- a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java +++ b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/MetadataService.java b/core/src/main/java/com/netflix/conductor/service/MetadataService.java index babd46627..e79fe6b69 100644 --- a/core/src/main/java/com/netflix/conductor/service/MetadataService.java +++ b/core/src/main/java/com/netflix/conductor/service/MetadataService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java index 7326ab62d..5c9a6357a 100644 --- a/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/TaskService.java b/core/src/main/java/com/netflix/conductor/service/TaskService.java index d89772659..f435fafef 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskService.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java index 5c07d5cff..ede5cd0fd 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java index e54c94f60..d49f05395 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java index c637b8bd9..1461b5a42 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java index 6ba2d7e64..65e63c627 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java index 99732a23a..255390d46 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java index 9dc3d334f..1c58ee535 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java b/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java index 56fbb22d3..5916e1275 100644 --- a/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java +++ b/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java index 2501849be..8588cb721 100644 --- a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java +++ b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy index d960eb2b5..609e58012 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy index f2bd2632c..421f3f5a7 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy index 837b81b16..6696b1449 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy index 1dd2eeec1..3652e4eec 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy index 9c6af719d..b78253677 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy index 88556b5a3..e0a0e93fb 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy b/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy index c95f8edb6..4a2d1b85b 100644 --- a/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy b/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy index 3b7763a23..b7e5ddabb 100644 --- a/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/TestUtils.java b/core/src/test/java/com/netflix/conductor/TestUtils.java index 08428322c..59b727b57 100644 --- a/core/src/test/java/com/netflix/conductor/TestUtils.java +++ b/core/src/test/java/com/netflix/conductor/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java b/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java index aceccf2b2..0cd66be9c 100644 --- a/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java +++ b/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java b/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java index 9391aea63..7bbc547ca 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java +++ b/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java b/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java index cad61a9bd..574929c41 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java +++ b/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java b/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java index e3a7351e8..207d6a826 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java b/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java index 2078b7c99..acf2bd77b 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java b/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java index ff446adaf..18769f879 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java index dbf0277f6..986625d17 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java index 133010f83..85f141882 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java index 2556e55d0..61c107026 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java index bc3da8c60..cb8f07868 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java b/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java index 3175a248e..972968c62 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java index cdacc2a25..893a540db 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java index f636b4547..8d06db695 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java index 8066db338..52fbc1703 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java index 9e5daa1a6..100181064 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java index 7c7f74796..192b78e6c 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java index e42270fd2..aa1415cb3 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java index da548e531..829e10352 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java index 2c3be681c..e389c373c 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java index 0c6c11872..6a7ad4151 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java index 4e3be843b..25cf9c3dd 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java index ed4d187b1..d1f17c331 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java index bd7dd268e..4386c1e5d 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java index edeef004e..106635960 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java index 6c89af44f..aa1c4dbaa 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java index 8d35c00ee..ee43ff652 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java index e3cc14d3c..98c1d41b0 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java index 9681bc2ea..61c6cde13 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java index 256c58b89..6a997fede 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java index 47e02a12e..9366b5870 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java index afdafa28d..6623997b6 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java index a1f2662db..c025afd35 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java index 2e9ac328c..405f224ba 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java index 50ef79f14..b9287266c 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java index b6a857fbe..ecedc54ea 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java index 1767f84ec..52d63fa65 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java index 61b735d2c..0f827fa55 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java index 5e11fb6ee..bf7d7e4a7 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java index 346b15c5b..26286aab5 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java index 7ecf0d9d2..347d73451 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java index e431a16e6..0ae2580f2 100644 --- a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java index 7c3859bcd..68639f310 100644 --- a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java +++ b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java index 7951d5e92..d5c9ca594 100644 --- a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java +++ b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java b/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java index e5fd2c04d..466436597 100644 --- a/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java +++ b/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java b/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java index 7fc95a54c..96f1c4379 100644 --- a/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java +++ b/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java index a093045d6..7f375fe13 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java index bc467b1a2..05e4e16ac 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java index 2f3c20751..57c8dd1b7 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java index 6633c5fa8..a3cad357a 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java b/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java index 21b03e190..30dfc8e91 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java index be49cce25..a61e3a946 100644 --- a/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java +++ b/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java b/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java index 97f4406b3..925690e3d 100644 --- a/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java +++ b/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java b/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java index 120130076..dc8cd7ad2 100644 --- a/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java +++ b/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java index 86a61e6ad..5f0b75448 100644 --- a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java b/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java index d767641ff..33f7e3a51 100644 --- a/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java index eed4afb4d..f6859dcdb 100644 --- a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java index 240b99e8c..6f8b3ecf8 100644 --- a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java index 1936401f1..25ea16088 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java index 772f2f2e9..566a710c7 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java index c66ed56df..8406deb05 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java index 84d2bcd59..932db00b2 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java index 9875f1776..b0e08f517 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java index d4ed352a7..43dad6b5e 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java index 3bbdb0fab..60f335599 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java index a41f38568..fb19cc6ca 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java index 66d3a26b4..d21d372ff 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java index 6abb4c742..c4227d2b4 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java index a137fd08d..f74817a9e 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java index 7cb3d7f3b..ea0377b73 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java index f4de4d36b..53d7d4045 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java index 67aa8965c..16b9e86c2 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java index b81c9f94b..726407a80 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java index bfbd3988f..f8d20c731 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java index f37c5f200..605a09c40 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java index 55d8d49a8..3c232f142 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java index edc9513c1..aafa3ad9f 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java index 97afc3684..fda089f06 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java index 87227a863..eae278c5f 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java index 589b62ccd..f54d78339 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java index 08a9d7b08..103096072 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java index 6315e00fa..94fc724e2 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java index a209c3ea7..38f32c627 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java index 4004be56f..8d20ecfe1 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java index f63110039..beedf07c5 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java index 2bbb4ed67..d214cdb8e 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java index df9ec6e95..1a75ccf88 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java index 5ca4f0284..5b543e7f7 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java index ed79b0f91..23f4ff625 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java index 412b7a8a6..6d5b5996a 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java index 6f8c70e11..69267162d 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java index 40c615f87..c1e3f9b9e 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java index ee5769990..148452127 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java index 26a192301..125c99f6c 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java index 5889cccd4..0868d4c75 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java index 0450547c5..5d182d1ef 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java index a781bc186..f3811c83c 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java index 83e51cd64..2f059c10b 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java index 0cbf37c99..6309e84ab 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java index 86c78e682..a32385e22 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java index 70a22c8f9..cbcb61b9f 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java index d55bd7b67..7e00a2795 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java index fe187f21d..5762193a3 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java index 4f62be3d8..83b3181bb 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java index 3b88e2056..c631cd831 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java index dee9bfdcb..edf4f4291 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java index 229af7d27..a82be1ae2 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java index 4b0cae8bb..a91dabf9b 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java index 6bd26d2de..9fad15dd7 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java index 369430d68..89c199496 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java index aaee4ac1b..6190847e8 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java index 3b10b3310..8abc17674 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java index 88967b17f..b48d827f9 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java index e5e7aa4eb..2d32fd82c 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java index 17417d029..0bc6f4403 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java index d388b165e..7c54edb1f 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java index 0a1cfd2f7..e559da67f 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java index 968904d2e..38176eb59 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java index 2b2f6a6df..7409d0501 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java index 3141e47e5..812fb0fd4 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java index 2258c2918..fdcbef968 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java index f5f32d6a4..975b855ee 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java index c0abd12df..e62eb597b 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java index 95d927b11..4bd0acf2a 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java index c22ba662a..1f8f56d77 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java index e5a5233d2..1744388cc 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java index e8a83b30a..a8bd0b873 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java index 452336431..bf0e1978f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java index 758445c03..6a8128cdd 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java index 009aa517a..6b507ea98 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java index 761835955..7a2665396 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java index caf33d8cf..7202b8dea 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java index 20634179e..428f29f1e 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java index ae1bb0777..ecea039e5 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java index f5d2c11ab..8381ee68a 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java index 7133e0ac3..e3650ac9f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java index 26a55fab7..de86607ff 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java index 69af0ddcb..ca61ae5a3 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java index b1685d395..550d03b65 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java index d1eff0580..bc1133831 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java index cca0246f7..f4cd6e279 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java index f60777aa2..fd9791ec1 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java index d4863dec2..743bbc1c3 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java index 369aa60f5..d6afcf182 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java index 2e54be19f..5f85e3fc6 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java index f0f34b2db..ae091168f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java index eff1f8bdd..1f5fae6f6 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java index f0f964330..447efea9a 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java index 01ca245fa..b5cdfbc6f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java index 1d15592b4..ae2594c04 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java index ae75b6c83..ac37eec0e 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java index d05e34b0a..296f528e6 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java index 411609864..ec056de40 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java index 17b8309de..bf86248af 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java index 850f1171d..cb5122bbb 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java index c03ac2f72..29fc37b0f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java index 61e01c0a0..8394e3f53 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java index d27fa8b7c..9d4bcd866 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java index a8c6ec378..3561cf53d 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java index 409b8e74f..c5f1bc764 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java index 41095b1a2..bb4421f24 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java index 2552d12b5..4628703a9 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java index 4230cac71..6581072f0 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java index 61cef6a81..338a8cc3d 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java index 194928a9a..e2985f1ce 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java index a7e818071..cba276042 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java index 066d74dbf..dbbc98e22 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java index 3566c49dc..235c57d51 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java index fa77df6b8..4d223ee23 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java index 3cb3375da..2b18bb3f9 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java index 3caa0d1df..26cdadf3a 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java index d691b5d9a..64a222f45 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java b/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java index 0cbed1c5d..5b83d5e8f 100644 --- a/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java +++ b/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java b/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java index 815993f77..be83116bd 100644 --- a/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java +++ b/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java index af6100b2a..85b800efb 100644 --- a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java index 870a8903a..6340c2e54 100644 --- a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java index 98bfacb5f..2b64c3f1a 100644 --- a/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java +++ b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java index 91fef3029..b5d905099 100644 --- a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java index 08828c444..3b9bb1a06 100644 --- a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java index 471bde2ff..0534d7436 100644 --- a/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java +++ b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/licenseheader.txt b/licenseheader.txt index 0edfbaf25..673d33532 100644 --- a/licenseheader.txt +++ b/licenseheader.txt @@ -1,5 +1,5 @@ /* - * Copyright $YEAR Netflix, Inc. + * Copyright $YEAR Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java index fb71cbc31..e0f8f4465 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java index 9b53ed523..287fd5d96 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java index a610f7978..a2e43333e 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java index 333af5f4f..4fc03a469 100644 --- a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java index 0a7324ada..0e336ea80 100644 --- a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java index fe5cf9795..42e21f705 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java index 7c926114b..5ea81ff67 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java index 2e6f76249..d4f6f6ed4 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java index 17731add1..45c13f397 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java index 10d543860..ef8bf2b09 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java index d84b0e310..b27beb917 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java index 668a8fa61..ca28f0c50 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java index da45247eb..84b5e28cc 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java index aefa2024f..24c9bbb2c 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java index 5dde942cf..2d462ed61 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java index a21c2ede4..5ec2b9011 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java index 2e8978e7c..098fad478 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java index 13908e342..8975f8641 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java index d61f972b7..a31a29ce1 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java index 09d1363b1..a6872b752 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java index d1e4715c6..0e9ddc0aa 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java index 738197cb5..473b1b64e 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java index e2d758157..25864ee20 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java index 5f3c17e68..6c17c1c8f 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java index 2b1b8a767..2614266ef 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java index 0bee790d4..220fd098f 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java index 492da53a4..95d72df4d 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java index 29de21e7b..787941a53 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java index 459ac8723..8c9c2d5af 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java index a5091cbdb..fef121898 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java index 49fb591c5..5dab5dd65 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java index 1d4b839c4..6c498576a 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java index 51750fc51..854ae5a05 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java index 9120d986a..9584d5b7e 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java index f0d26d285..f9b24cb16 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java index b6bc20a60..a31b98326 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java index 0f4feff1b..0ee381914 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java index cc3e7de90..a9f735ac5 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java index 9b6d346fa..bb96ac01b 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java index fa8c68496..967d77dac 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java index 007a00cce..1150b31a0 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java index 1c485db90..50feebfc3 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java index 0df04083a..67779bd44 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java index c39e703d5..86f6a5871 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java index 31dbf11d5..9ec2a14c9 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java index c88cfb8cb..916bd2bb3 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java index f41e55a57..85b8dcce4 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java index 1166bd4f8..04a9572a1 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java index 57c3ccf77..06173b9df 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java index a2a358fa2..9728a9df6 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java index d50407536..43fa778db 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java index ab8c5acd1..0ca4e8e9e 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java index 206bd8569..67dfbcdb6 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java index 829bbded2..a9640217a 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java index f0c3b3999..f68a51320 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java index 2b2370950..651bfd84e 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java index 74ce8c7ee..2139c09d7 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java index d65115c46..beabb3a2d 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java index d0f16e31a..6ce75615c 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java index 838146ffe..1878f2b11 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java index c790947ed..a6e5a2ca6 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java index b1563b38e..f8e90bc09 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java index 73f62b68d..a2a891290 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java index f86ae78bd..4c15590b5 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java index da22860e8..4bb282782 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java index 1825fbb01..d68aaa626 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java index fdce7a3f2..edc235514 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java index 98320e851..19b5d2074 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java index ceb7fec10..d8f09c379 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java index 9349093eb..b43bdc5fe 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java index 20b0e929d..c51e95d11 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy b/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy index 9cb4d4239..7abeb2c10 100644 --- a/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy +++ b/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java index 7d03890e6..fb812ccb3 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java index c7ea9e983..b979c5025 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java index 28cdcfef8..ef833866d 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java index 24953dd7b..4e6e06237 100644 --- a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java +++ b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java index 0303c9f80..9382acae6 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java index 410f96f16..43c8bb6f4 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java index 1d03de008..4eb5906fb 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java index 5f4783406..a8831e039 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java index b98e038f7..dd1749324 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java index 94883f6f5..9aad3a198 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java index 905fd65aa..c06612ece 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java index aa35f5e67..1b3762d3d 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java index 5d07cc34f..6ddc53d6c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java index bdf881fbb..f36af6d06 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java index 3901931dc..1fffaaaa6 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java index e0aa0bfd8..c8d6aac9e 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java index 33902640d..0ad4f952f 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java index f7951c1e3..df715582e 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java index 717e582a7..2c04307da 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java index 9535dc4cc..58779153f 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java index 9e32a1d5f..ff2094876 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java index 7f1823af8..eb37a609e 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java index 2ba4528bb..96cd6642a 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java index b757f88c7..aec6f1357 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java index 169146be3..df57b925c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java index 38abcb964..0b3f7c2bb 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java index 50f603228..9c6b2906a 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java index 97b326e44..ca5a9e528 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java index 2ec4b3470..cce15fc72 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java index a3ce44e52..c56d57d59 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java index 6efa7ccd4..7d55599a2 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java index 6b53f00e6..c4ab990cd 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java index 7bc844f39..0065ed6e7 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java index fd9119162..64c7722c8 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java index 3553856ce..8027529ad 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java index 30877fc18..28b6e4762 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java index 7760c68a3..5e13177df 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java index e69b22a46..94df53d6d 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java index b38e13468..6f5099abc 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java b/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java index 940d3fb92..1751d3e4f 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java +++ b/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java b/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java index e4b001591..f54f178f9 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java +++ b/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java index 4221917c5..ecd36c5d1 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java index f61b8e95d..18c249e26 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java index 02b620c1e..43c5b480c 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java index ffd3767ad..ddd602a5e 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java index 023ed2b57..0fa1defc0 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java index 70eb05872..869998d2c 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java index d9f818f44..e7ab74e9b 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java index 928e7419f..e70bffc07 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java index 409328e81..3077824a6 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java index 263404b2d..713e46040 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java b/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java index e69d9cada..9246d9852 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java +++ b/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java index 83639be47..79137832e 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java index ae1adffb6..6a42354dd 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java index 36d5b6e70..8189e6b56 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java index b9dd18d23..7ed7f6bfb 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java index 60759bc62..f52a9fe17 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/server/src/main/java/com/netflix/conductor/Conductor.java b/server/src/main/java/com/netflix/conductor/Conductor.java index aa55b7804..bf89417c9 100644 --- a/server/src/main/java/com/netflix/conductor/Conductor.java +++ b/server/src/main/java/com/netflix/conductor/Conductor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java b/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java index a02b7ce34..b7996c109 100644 --- a/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java +++ b/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy index d3270c447..d40f8619f 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy index 6d43a7424..3f4cfbdde 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy index 2d46ae360..28a0ecf8d 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy index 33ba261c8..98e8596e3 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy index d7dac2993..8ce0e1457 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy index 189eeaaf0..860940570 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy index e4681dcc9..d3d98a2ce 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy index ea1393975..da9810330 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy index c5168ef70..8ad31e163 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy index 437f4e1cc..2fb7a4932 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy index 5e6d8c0be..f6b96e51c 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy index 61fdd055e..b60b9fddf 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy index 5bd155249..ed1c4a54c 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy index 006c367f0..66f70d048 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy index 281b2d71d..46dbce0a6 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy index 61eca08ab..cdd5ef493 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy index 714300f48..cdd1e8091 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy index 539a3d9d6..ba59c3529 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy index 0752a2b43..2e4fe6250 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy index c6367a957..4a1bfe128 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy index 550c225d6..43adfeeb3 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy index 0445a4777..bd740a1e2 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy index 0a74d88ff..7f5a79671 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy index cffc9b615..39666558e 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy index 926a1ded4..086452e25 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy index 0211821d1..1ea47e6ce 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy index fea312fa0..7cb9a9ce4 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy index 9a22b20fb..fc6f4069b 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy index 0fbf0ec0b..81e752713 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy index 44f7ade06..46337739f 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy index 45f26e5ae..9b4fe2a3d 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy index 4f13a99df..3f9e911ff 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java index 51cbc9ac5..2688b560e 100644 --- a/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java +++ b/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 150e59947..0b96b2dc8 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index 3d4a23e1d..88110d39c 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java index 0353cd048..f3f4b6271 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java index e04a2a991..c05873da9 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java index 0dded2cdd..9e49a5b8d 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java b/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java index f93baf6d2..86c7be171 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java b/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java index d0af23206..a12be85ab 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java index 8a86d2fa0..892cdbdae 100644 --- a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java +++ b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java index 59f91c41b..90fe5379f 100644 --- a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java +++ b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 59702412b..5d4f248b6 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index f66cfa3cd..d21148311 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java index cc58948c1..791a6c5f2 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java index 50978417d..017fb69e3 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java index 53361429a..18e0b4ba1 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java index c784c38a4..0745e2604 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java index d10bf5f73..f58578fed 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java index 8cdf76f14..dbb941f88 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java index 4073ce7ad..f714e2387 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java index 7532fb01d..551daaec8 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java index 4f9f002b6..be5ec0aa1 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Netflix, Inc. + * Copyright 2023 Conductor 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 From 83b7eef9b3de24f12c7a1892cd0c8be1ea051bd1 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:41:30 -0800 Subject: [PATCH 056/202] fix typo --- CONTRIBUTING.md | 61 ++++++++++++++++++- .../contribs/queue/amqp/AMQPConnection.java | 2 +- .../queue/amqp/AMQPObservableQueue.java | 2 +- .../config/AMQPEventQueueConfiguration.java | 2 +- .../amqp/config/AMQPEventQueueProperties.java | 2 +- .../amqp/config/AMQPEventQueueProvider.java | 2 +- .../queue/amqp/config/AMQPRetryPattern.java | 2 +- .../queue/amqp/util/AMQPConfigurations.java | 2 +- .../queue/amqp/util/AMQPConstants.java | 2 +- .../queue/amqp/util/AMQPSettings.java | 2 +- .../queue/amqp/util/ConnectionType.java | 2 +- .../contribs/queue/amqp/util/RetryType.java | 2 +- .../amqp/AMQPEventQueueProviderTest.java | 2 +- .../queue/amqp/AMQPObservableQueueTest.java | 2 +- .../contribs/queue/amqp/AMQPSettingsTest.java | 2 +- .../src/example/java/com/example/Example.java | 2 +- .../protogen/AbstractMessage.java | 2 +- .../annotationsprocessor/protogen/Enum.java | 2 +- .../protogen/Message.java | 2 +- .../protogen/ProtoFile.java | 2 +- .../protogen/ProtoGen.java | 2 +- .../protogen/ProtoGenTask.java | 2 +- .../protogen/types/AbstractType.java | 2 +- .../protogen/types/ExternMessageType.java | 2 +- .../protogen/types/GenericType.java | 2 +- .../protogen/types/ListType.java | 2 +- .../protogen/types/MapType.java | 2 +- .../protogen/types/MessageType.java | 2 +- .../protogen/types/ScalarType.java | 2 +- .../protogen/types/TypeMapper.java | 2 +- .../protogen/types/WrappedType.java | 2 +- .../protogen/ProtoGenTest.java | 2 +- .../annotations/protogen/ProtoEnum.java | 2 +- .../annotations/protogen/ProtoField.java | 2 +- .../annotations/protogen/ProtoMessage.java | 2 +- .../conductor/s3/config/S3Configuration.java | 2 +- .../conductor/s3/config/S3Properties.java | 2 +- .../s3/storage/S3PayloadStorage.java | 2 +- .../config/SQSEventQueueConfiguration.java | 2 +- .../sqs/config/SQSEventQueueProperties.java | 2 +- .../sqs/config/SQSEventQueueProvider.java | 2 +- .../sqs/eventqueue/SQSObservableQueue.java | 2 +- .../DefaultEventQueueProcessorTest.java | 2 +- .../eventqueue/SQSObservableQueueTest.java | 2 +- .../config/AzureBlobConfiguration.java | 2 +- .../azureblob/config/AzureBlobProperties.java | 2 +- .../storage/AzureBlobPayloadStorage.java | 2 +- .../storage/AzureBlobPayloadStorageTest.java | 2 +- .../config/CassandraConfiguration.java | 2 +- .../cassandra/config/CassandraProperties.java | 2 +- .../cache/CacheableEventHandlerDAO.java | 2 +- .../config/cache/CacheableMetadataDAO.java | 2 +- .../cassandra/config/cache/CachingConfig.java | 2 +- .../cassandra/dao/CassandraBaseDAO.java | 2 +- .../dao/CassandraEventHandlerDAO.java | 2 +- .../cassandra/dao/CassandraExecutionDAO.java | 2 +- .../cassandra/dao/CassandraMetadataDAO.java | 2 +- .../cassandra/dao/CassandraPollDataDAO.java | 2 +- .../conductor/cassandra/util/Constants.java | 2 +- .../conductor/cassandra/util/Statements.java | 2 +- .../dao/CassandraEventHandlerDAOSpec.groovy | 2 +- .../dao/CassandraExecutionDAOSpec.groovy | 2 +- .../dao/CassandraMetadataDAOSpec.groovy | 2 +- .../cassandra/dao/CassandraSpec.groovy | 2 +- .../cassandra/util/StatementsSpec.groovy | 2 +- .../client/spring/ClientProperties.java | 2 +- .../ConductorClientAutoConfiguration.java | 2 +- .../ConductorWorkerAutoConfiguration.java | 2 +- .../spring/SpringWorkerConfiguration.java | 2 +- .../client/spring/ExampleClient.java | 2 +- .../conductor/client/spring/Workers.java | 2 +- .../client/automator/PollingSemaphore.java | 2 +- .../client/automator/TaskPollExecutor.java | 2 +- .../automator/TaskRunnerConfigurer.java | 2 +- .../config/ConductorClientConfiguration.java | 2 +- .../DefaultConductorClientConfiguration.java | 2 +- .../client/config/PropertyFactory.java | 2 +- .../exception/ConductorClientException.java | 2 +- .../conductor/client/http/ClientBase.java | 2 +- .../client/http/ClientRequestHandler.java | 2 +- .../conductor/client/http/EventClient.java | 2 +- .../conductor/client/http/MetadataClient.java | 2 +- .../conductor/client/http/PayloadStorage.java | 2 +- .../conductor/client/http/TaskClient.java | 2 +- .../conductor/client/http/WorkflowClient.java | 2 +- .../client/telemetry/MetricsContainer.java | 2 +- .../conductor/client/worker/Worker.java | 2 +- .../client/http/ClientSpecification.groovy | 2 +- .../client/http/EventClientSpec.groovy | 2 +- .../client/http/MetadataClientSpec.groovy | 2 +- .../client/http/TaskClientSpec.groovy | 2 +- .../client/http/WorkflowClientSpec.groovy | 2 +- .../automator/PollingSemaphoreTest.java | 2 +- .../automator/TaskPollExecutorTest.java | 2 +- .../automator/TaskRunnerConfigurerTest.java | 2 +- .../client/config/TestPropertyFactory.java | 2 +- .../netflix/conductor/client/sample/Main.java | 2 +- .../conductor/client/sample/SampleWorker.java | 2 +- .../client/testing/AbstractWorkflowTests.java | 2 +- .../client/testing/LoanWorkflowInput.java | 2 +- .../client/testing/LoanWorkflowTest.java | 2 +- .../client/testing/RegressionTest.java | 2 +- .../client/testing/SubWorkflowTest.java | 2 +- .../client/worker/TestWorkflowTask.java | 2 +- .../conductor/dao/ExecutionDAOTest.java | 2 +- .../com/netflix/conductor/dao/TestBase.java | 2 +- .../ObjectMapperBuilderConfiguration.java | 2 +- .../config/ObjectMapperConfiguration.java | 2 +- .../common/config/ObjectMapperProvider.java | 2 +- .../constraints/NoSemiColonConstraint.java | 2 +- .../OwnerEmailMandatoryConstraint.java | 2 +- .../TaskReferenceNameUniqueConstraint.java | 2 +- .../constraints/TaskTimeoutConstraint.java | 2 +- .../common/jackson/JsonProtoModule.java | 2 +- .../conductor/common/metadata/Auditable.java | 2 +- .../conductor/common/metadata/BaseDef.java | 2 +- .../common/metadata/acl/Permission.java | 2 +- .../metadata/events/EventExecution.java | 2 +- .../common/metadata/events/EventHandler.java | 2 +- .../common/metadata/tasks/PollData.java | 2 +- .../conductor/common/metadata/tasks/Task.java | 2 +- .../common/metadata/tasks/TaskDef.java | 2 +- .../common/metadata/tasks/TaskExecLog.java | 2 +- .../common/metadata/tasks/TaskResult.java | 2 +- .../common/metadata/tasks/TaskType.java | 2 +- .../workflow/DynamicForkJoinTask.java | 2 +- .../workflow/DynamicForkJoinTaskList.java | 2 +- .../workflow/RerunWorkflowRequest.java | 2 +- .../metadata/workflow/SkipTaskRequest.java | 2 +- .../workflow/StartWorkflowRequest.java | 2 +- .../metadata/workflow/SubWorkflowParams.java | 2 +- .../common/metadata/workflow/WorkflowDef.java | 2 +- .../metadata/workflow/WorkflowDefSummary.java | 2 +- .../metadata/workflow/WorkflowTask.java | 2 +- .../conductor/common/model/BulkResponse.java | 2 +- .../common/run/ExternalStorageLocation.java | 2 +- .../conductor/common/run/SearchResult.java | 2 +- .../conductor/common/run/TaskSummary.java | 2 +- .../conductor/common/run/Workflow.java | 2 +- .../conductor/common/run/WorkflowSummary.java | 2 +- .../common/run/WorkflowTestRequest.java | 2 +- .../common/utils/ConstraintParamUtil.java | 2 +- .../conductor/common/utils/EnvUtils.java | 2 +- .../common/utils/ExternalPayloadStorage.java | 2 +- .../conductor/common/utils/SummaryUtil.java | 2 +- .../conductor/common/utils/TaskUtils.java | 2 +- .../common/validation/ErrorResponse.java | 2 +- .../common/validation/ValidationError.java | 2 +- .../config/TestObjectMapperConfiguration.java | 2 +- .../common/events/EventHandlerTest.java | 2 +- .../conductor/common/run/TaskSummaryTest.java | 2 +- .../conductor/common/tasks/TaskDefTest.java | 2 +- .../common/tasks/TaskResultTest.java | 2 +- .../conductor/common/tasks/TaskTest.java | 2 +- .../common/utils/ConstraintParamUtilTest.java | 2 +- .../common/utils/SummaryUtilTest.java | 2 +- .../workflow/SubWorkflowParamsTest.java | 2 +- .../workflow/WorkflowDefValidatorTest.java | 2 +- .../common/workflow/WorkflowTaskTest.java | 2 +- .../netflix/conductor/annotations/Audit.java | 2 +- .../netflix/conductor/annotations/Trace.java | 2 +- .../annotations/VisibleForTesting.java | 2 +- .../core/LifecycleAwareComponent.java | 2 +- .../conductor/core/WorkflowContext.java | 2 +- .../config/ConductorCoreConfiguration.java | 2 +- .../core/config/ConductorProperties.java | 2 +- .../core/config/SchedulerConfiguration.java | 2 +- .../core/dal/ExecutionDAOFacade.java | 2 +- .../core/event/WorkflowCreationEvent.java | 2 +- .../core/event/WorkflowEvaluationEvent.java | 2 +- .../core/events/ActionProcessor.java | 2 +- .../core/events/DefaultEventProcessor.java | 2 +- .../core/events/DefaultEventQueueManager.java | 2 +- .../core/events/EventQueueManager.java | 2 +- .../core/events/EventQueueProvider.java | 2 +- .../conductor/core/events/EventQueues.java | 2 +- .../core/events/ScriptEvaluator.java | 2 +- .../core/events/SimpleActionProcessor.java | 2 +- .../queue/ConductorEventQueueProvider.java | 2 +- .../queue/ConductorObservableQueue.java | 2 +- .../queue/DefaultEventQueueProcessor.java | 2 +- .../conductor/core/events/queue/Message.java | 2 +- .../core/events/queue/ObservableQueue.java | 2 +- .../core/exception/ConflictException.java | 2 +- .../core/exception/NonTransientException.java | 2 +- .../core/exception/NotFoundException.java | 2 +- .../exception/TerminateWorkflowException.java | 2 +- .../core/exception/TransientException.java | 2 +- .../execution/AsyncSystemTaskExecutor.java | 2 +- .../core/execution/DeciderService.java | 2 +- .../core/execution/StartWorkflowInput.java | 2 +- .../core/execution/WorkflowExecutor.java | 2 +- .../core/execution/evaluators/Evaluator.java | 2 +- .../evaluators/JavascriptEvaluator.java | 2 +- .../evaluators/ValueParamEvaluator.java | 2 +- .../execution/mapper/DecisionTaskMapper.java | 2 +- .../execution/mapper/DoWhileTaskMapper.java | 2 +- .../execution/mapper/DynamicTaskMapper.java | 2 +- .../execution/mapper/EventTaskMapper.java | 2 +- .../mapper/ExclusiveJoinTaskMapper.java | 2 +- .../mapper/ForkJoinDynamicTaskMapper.java | 2 +- .../execution/mapper/ForkJoinTaskMapper.java | 2 +- .../core/execution/mapper/HTTPTaskMapper.java | 2 +- .../execution/mapper/HumanTaskMapper.java | 2 +- .../execution/mapper/InlineTaskMapper.java | 2 +- .../core/execution/mapper/JoinTaskMapper.java | 2 +- .../mapper/JsonJQTransformTaskMapper.java | 2 +- .../mapper/KafkaPublishTaskMapper.java | 2 +- .../execution/mapper/LambdaTaskMapper.java | 2 +- .../core/execution/mapper/NoopTaskMapper.java | 2 +- .../mapper/SetVariableTaskMapper.java | 2 +- .../execution/mapper/SimpleTaskMapper.java | 2 +- .../mapper/StartWorkflowTaskMapper.java | 2 +- .../mapper/SubWorkflowTaskMapper.java | 2 +- .../execution/mapper/SwitchTaskMapper.java | 2 +- .../core/execution/mapper/TaskMapper.java | 2 +- .../execution/mapper/TaskMapperContext.java | 2 +- .../execution/mapper/TerminateTaskMapper.java | 2 +- .../mapper/UserDefinedTaskMapper.java | 2 +- .../core/execution/mapper/WaitTaskMapper.java | 2 +- .../core/execution/tasks/Decision.java | 2 +- .../core/execution/tasks/DoWhile.java | 2 +- .../conductor/core/execution/tasks/Event.java | 2 +- .../core/execution/tasks/ExclusiveJoin.java | 2 +- .../core/execution/tasks/ExecutionConfig.java | 2 +- .../conductor/core/execution/tasks/Fork.java | 2 +- .../conductor/core/execution/tasks/Human.java | 2 +- .../core/execution/tasks/Inline.java | 2 +- .../tasks/IsolatedTaskQueueProducer.java | 2 +- .../conductor/core/execution/tasks/Join.java | 2 +- .../core/execution/tasks/Lambda.java | 2 +- .../conductor/core/execution/tasks/Noop.java | 2 +- .../core/execution/tasks/SetVariable.java | 2 +- .../core/execution/tasks/StartWorkflow.java | 2 +- .../core/execution/tasks/SubWorkflow.java | 2 +- .../core/execution/tasks/Switch.java | 2 +- .../execution/tasks/SystemTaskRegistry.java | 2 +- .../execution/tasks/SystemTaskWorker.java | 2 +- .../tasks/SystemTaskWorkerCoordinator.java | 2 +- .../core/execution/tasks/Terminate.java | 2 +- .../conductor/core/execution/tasks/Wait.java | 2 +- .../execution/tasks/WorkflowSystemTask.java | 2 +- .../conductor/core/index/NoopIndexDAO.java | 2 +- .../core/index/NoopIndexDAOConfiguration.java | 2 +- .../core/listener/TaskStatusListener.java | 2 +- .../core/listener/TaskStatusListenerStub.java | 2 +- .../core/listener/WorkflowStatusListener.java | 2 +- .../listener/WorkflowStatusListenerStub.java | 2 +- .../core/metadata/MetadataMapperService.java | 2 +- .../operation/StartWorkflowOperation.java | 2 +- .../core/operation/WorkflowOperation.java | 2 +- .../reconciliation/WorkflowReconciler.java | 2 +- .../reconciliation/WorkflowRepairService.java | 2 +- .../core/reconciliation/WorkflowSweeper.java | 2 +- .../core/storage/DummyPayloadStorage.java | 2 +- .../com/netflix/conductor/core/sync/Lock.java | 2 +- .../core/sync/local/LocalOnlyLock.java | 2 +- .../local/LocalOnlyLockConfiguration.java | 2 +- .../conductor/core/sync/noop/NoopLock.java | 2 +- .../conductor/core/utils/DateTimeUtils.java | 2 +- .../utils/ExternalPayloadStorageUtils.java | 2 +- .../conductor/core/utils/IDGenerator.java | 2 +- .../conductor/core/utils/JsonUtils.java | 2 +- .../conductor/core/utils/ParametersUtils.java | 2 +- .../conductor/core/utils/QueueUtils.java | 2 +- .../conductor/core/utils/SemaphoreUtil.java | 2 +- .../netflix/conductor/core/utils/Utils.java | 2 +- .../dao/ConcurrentExecutionLimitDAO.java | 2 +- .../conductor/dao/EventHandlerDAO.java | 2 +- .../netflix/conductor/dao/ExecutionDAO.java | 2 +- .../com/netflix/conductor/dao/IndexDAO.java | 2 +- .../netflix/conductor/dao/MetadataDAO.java | 2 +- .../netflix/conductor/dao/PollDataDAO.java | 2 +- .../com/netflix/conductor/dao/QueueDAO.java | 2 +- .../conductor/dao/RateLimitingDAO.java | 2 +- .../netflix/conductor/metrics/Monitors.java | 2 +- .../conductor/metrics/WorkflowMonitor.java | 2 +- .../netflix/conductor/model/TaskModel.java | 2 +- .../conductor/model/WorkflowModel.java | 2 +- .../conductor/service/AdminService.java | 2 +- .../conductor/service/AdminServiceImpl.java | 2 +- .../conductor/service/EventService.java | 2 +- .../conductor/service/EventServiceImpl.java | 2 +- .../service/ExecutionLockService.java | 2 +- .../conductor/service/ExecutionService.java | 2 +- .../conductor/service/MetadataService.java | 2 +- .../service/MetadataServiceImpl.java | 2 +- .../conductor/service/TaskService.java | 2 +- .../conductor/service/TaskServiceImpl.java | 2 +- .../service/WorkflowBulkService.java | 2 +- .../service/WorkflowBulkServiceImpl.java | 2 +- .../conductor/service/WorkflowService.java | 2 +- .../service/WorkflowServiceImpl.java | 2 +- .../service/WorkflowTestService.java | 2 +- .../validations/ValidationContext.java | 2 +- .../WorkflowTaskTypeConstraint.java | 2 +- .../AsyncSystemTaskExecutorTest.groovy | 2 +- .../core/execution/tasks/DoWhileSpec.groovy | 2 +- .../core/execution/tasks/EventSpec.groovy | 2 +- .../IsolatedTaskQueueProducerSpec.groovy | 2 +- .../execution/tasks/StartWorkflowSpec.groovy | 2 +- .../StartWorkflowOperationSpec.groovy | 2 +- .../conductor/model/TaskModelSpec.groovy | 2 +- .../conductor/model/WorkflowModelSpec.groovy | 2 +- .../java/com/netflix/conductor/TestUtils.java | 2 +- .../core/dal/ExecutionDAOFacadeTest.java | 2 +- .../core/events/MockObservableQueue.java | 2 +- .../core/events/MockQueueProvider.java | 2 +- .../events/TestDefaultEventProcessor.java | 2 +- .../conductor/core/events/TestScriptEval.java | 2 +- .../events/TestSimpleActionProcessor.java | 2 +- .../core/execution/TestDeciderOutcomes.java | 2 +- .../core/execution/TestDeciderService.java | 2 +- .../core/execution/TestWorkflowDef.java | 2 +- .../core/execution/TestWorkflowExecutor.java | 2 +- .../execution/WorkflowSystemTaskStub.java | 2 +- .../mapper/DecisionTaskMapperTest.java | 2 +- .../mapper/DoWhileTaskMapperTest.java | 2 +- .../mapper/DynamicTaskMapperTest.java | 2 +- .../execution/mapper/EventTaskMapperTest.java | 2 +- .../mapper/ForkJoinDynamicTaskMapperTest.java | 2 +- .../mapper/ForkJoinTaskMapperTest.java | 2 +- .../execution/mapper/HTTPTaskMapperTest.java | 2 +- .../execution/mapper/HumanTaskMapperTest.java | 2 +- .../mapper/InlineTaskMapperTest.java | 2 +- .../execution/mapper/JoinTaskMapperTest.java | 2 +- .../mapper/JsonJQTransformTaskMapperTest.java | 2 +- .../mapper/KafkaPublishTaskMapperTest.java | 2 +- .../mapper/LambdaTaskMapperTest.java | 2 +- .../execution/mapper/NoopTaskMapperTest.java | 2 +- .../mapper/SetVariableTaskMapperTest.java | 2 +- .../mapper/SimpleTaskMapperTest.java | 2 +- .../mapper/SubWorkflowTaskMapperTest.java | 2 +- .../mapper/SwitchTaskMapperTest.java | 2 +- .../mapper/TerminateTaskMapperTest.java | 2 +- .../mapper/UserDefinedTaskMapperTest.java | 2 +- .../execution/mapper/WaitTaskMapperTest.java | 2 +- .../tasks/EventQueueResolutionTest.java | 2 +- .../core/execution/tasks/InlineTest.java | 2 +- .../core/execution/tasks/TestLambda.java | 2 +- .../core/execution/tasks/TestNoop.java | 2 +- .../core/execution/tasks/TestSubWorkflow.java | 2 +- .../execution/tasks/TestSystemTaskWorker.java | 2 +- .../TestSystemTaskWorkerCoordinator.java | 2 +- .../core/execution/tasks/TestTerminate.java | 2 +- .../metadata/MetadataMapperServiceTest.java | 2 +- .../TestWorkflowRepairService.java | 2 +- .../reconciliation/TestWorkflowSweeper.java | 2 +- .../core/storage/DummyPayloadStorageTest.java | 2 +- .../core/sync/local/LocalOnlyLockTest.java | 2 +- .../ExternalPayloadStorageUtilsTest.java | 2 +- .../conductor/core/utils/JsonUtilsTest.java | 2 +- .../core/utils/ParametersUtilsTest.java | 2 +- .../conductor/core/utils/QueueUtilsTest.java | 2 +- .../core/utils/SemaphoreUtilTest.java | 2 +- .../conductor/dao/ExecutionDAOTest.java | 2 +- .../conductor/dao/PollDataDAOTest.java | 2 +- .../metrics/WorkflowMonitorTest.java | 2 +- .../conductor/service/EventServiceTest.java | 2 +- .../service/ExecutionServiceTest.java | 2 +- .../service/MetadataServiceTest.java | 2 +- .../conductor/service/TaskServiceTest.java | 2 +- .../service/WorkflowBulkServiceTest.java | 2 +- .../service/WorkflowServiceTest.java | 2 +- .../WorkflowDefConstraintTest.java | 2 +- .../WorkflowTaskTypeConstraintTest.java | 2 +- docs/docs/resources/contributing.md | 19 +----- .../es7/config/ElasticSearchConditions.java | 2 +- .../es7/config/ElasticSearchProperties.java | 2 +- .../config/ElasticSearchV7Configuration.java | 2 +- .../dao/index/BulkRequestBuilderWrapper.java | 2 +- .../es7/dao/index/BulkRequestWrapper.java | 2 +- .../es7/dao/index/ElasticSearchBaseDAO.java | 2 +- .../es7/dao/index/ElasticSearchRestDAOV7.java | 2 +- .../es7/dao/query/parser/Expression.java | 2 +- .../es7/dao/query/parser/FilterProvider.java | 2 +- .../dao/query/parser/GroupedExpression.java | 2 +- .../es7/dao/query/parser/NameValue.java | 2 +- .../query/parser/internal/AbstractNode.java | 2 +- .../dao/query/parser/internal/BooleanOp.java | 2 +- .../query/parser/internal/ComparisonOp.java | 2 +- .../dao/query/parser/internal/ConstValue.java | 2 +- .../internal/FunctionThrowingException.java | 2 +- .../dao/query/parser/internal/ListConst.java | 2 +- .../es7/dao/query/parser/internal/Name.java | 2 +- .../parser/internal/ParserException.java | 2 +- .../es7/dao/query/parser/internal/Range.java | 2 +- .../index/ElasticSearchRestDaoBaseTest.java | 2 +- .../es7/dao/index/ElasticSearchTest.java | 2 +- .../index/TestBulkRequestBuilderWrapper.java | 2 +- .../dao/index/TestElasticSearchRestDAOV7.java | 2 +- .../TestElasticSearchRestDAOV7Batch.java | 2 +- .../es7/dao/query/parser/TestExpression.java | 2 +- .../query/parser/TestGroupedExpression.java | 2 +- .../parser/internal/AbstractParserTest.java | 2 +- .../query/parser/internal/TestBooleanOp.java | 2 +- .../parser/internal/TestComparisonOp.java | 2 +- .../query/parser/internal/TestConstValue.java | 2 +- .../dao/query/parser/internal/TestName.java | 2 +- .../conductor/es7/utils/TestUtils.java | 2 +- .../conductor/client/grpc/ClientBase.java | 2 +- .../conductor/client/grpc/EventClient.java | 2 +- .../conductor/client/grpc/MetadataClient.java | 2 +- .../conductor/client/grpc/TaskClient.java | 2 +- .../conductor/client/grpc/WorkflowClient.java | 2 +- .../client/grpc/EventClientTest.java | 2 +- .../conductor/client/grpc/TaskClientTest.java | 2 +- .../client/grpc/WorkflowClientTest.java | 2 +- .../conductor/grpc/server/GRPCServer.java | 2 +- .../grpc/server/GRPCServerProperties.java | 2 +- .../grpc/server/GrpcConfiguration.java | 2 +- .../grpc/server/service/EventServiceImpl.java | 2 +- .../grpc/server/service/GRPCHelper.java | 2 +- .../server/service/HealthServiceImpl.java | 2 +- .../server/service/MetadataServiceImpl.java | 2 +- .../grpc/server/service/TaskServiceImpl.java | 2 +- .../server/service/WorkflowServiceImpl.java | 2 +- .../server/service/HealthServiceImplTest.java | 2 +- .../server/service/TaskServiceImplTest.java | 2 +- .../service/WorkflowServiceImplTest.java | 2 +- .../conductor/tasks/http/HttpTask.java | 2 +- .../DefaultRestTemplateProvider.java | 2 +- .../http/providers/RestTemplateProvider.java | 2 +- .../conductor/tasks/http/HttpTaskTest.java | 2 +- .../DefaultRestTemplateProviderTest.java | 2 +- .../conductor/sdk/example/shipment/Order.java | 2 +- .../sdk/example/shipment/Shipment.java | 2 +- .../sdk/example/shipment/ShipmentState.java | 2 +- .../sdk/example/shipment/ShipmentWorkers.java | 2 +- .../example/shipment/ShipmentWorkflow.java | 2 +- .../conductor/sdk/example/shipment/User.java | 2 +- .../sdk/healthcheck/HealthCheckClient.java | 2 +- .../sdk/testing/LocalServerRunner.java | 2 +- .../sdk/testing/WorkflowTestRunner.java | 2 +- .../sdk/workflow/def/ConductorWorkflow.java | 2 +- .../sdk/workflow/def/ValidationError.java | 2 +- .../sdk/workflow/def/WorkflowBuilder.java | 2 +- .../sdk/workflow/def/tasks/DoWhile.java | 2 +- .../sdk/workflow/def/tasks/Dynamic.java | 2 +- .../sdk/workflow/def/tasks/DynamicFork.java | 2 +- .../workflow/def/tasks/DynamicForkInput.java | 2 +- .../sdk/workflow/def/tasks/Event.java | 2 +- .../sdk/workflow/def/tasks/ForkJoin.java | 2 +- .../sdk/workflow/def/tasks/Http.java | 2 +- .../conductor/sdk/workflow/def/tasks/JQ.java | 2 +- .../sdk/workflow/def/tasks/Javascript.java | 2 +- .../sdk/workflow/def/tasks/Join.java | 2 +- .../sdk/workflow/def/tasks/SetVariable.java | 2 +- .../sdk/workflow/def/tasks/SimpleTask.java | 2 +- .../sdk/workflow/def/tasks/SubWorkflow.java | 2 +- .../sdk/workflow/def/tasks/Switch.java | 2 +- .../sdk/workflow/def/tasks/Task.java | 2 +- .../sdk/workflow/def/tasks/TaskRegistry.java | 2 +- .../sdk/workflow/def/tasks/Terminate.java | 2 +- .../sdk/workflow/def/tasks/Wait.java | 2 +- .../workflow/executor/WorkflowExecutor.java | 2 +- .../executor/task/AnnotatedWorker.java | 2 +- .../task/AnnotatedWorkerExecutor.java | 2 +- .../executor/task/DynamicForkWorker.java | 2 +- .../executor/task/NonRetryableException.java | 2 +- .../workflow/executor/task/TaskContext.java | 2 +- .../executor/task/WorkerConfiguration.java | 2 +- .../sdk/workflow/task/InputParam.java | 2 +- .../sdk/workflow/task/OutputParam.java | 2 +- .../sdk/workflow/task/WorkerTask.java | 2 +- .../sdk/workflow/utils/InputOutputGetter.java | 2 +- .../sdk/workflow/utils/MapBuilder.java | 2 +- .../workflow/utils/ObjectMapperProvider.java | 2 +- .../workflow/def/TaskConversionsTests.java | 2 +- .../workflow/def/WorkflowCreationTests.java | 2 +- .../workflow/def/WorkflowDefTaskTests.java | 2 +- .../sdk/workflow/def/WorkflowState.java | 2 +- .../executor/task/AnnotatedWorkerTests.java | 2 +- .../executor/task/TestWorkerConfig.java | 2 +- .../sdk/workflow/testing/Task1Input.java | 2 +- .../workflow/testing/TestWorkflowInput.java | 2 +- .../testing/WorkflowTestFrameworkTests.java | 2 +- .../conductor/tasks/json/JsonJqTransform.java | 2 +- .../tasks/json/JsonJqTransformTest.java | 2 +- .../tasks/kafka/KafkaProducerManager.java | 2 +- .../tasks/kafka/KafkaPublishTask.java | 2 +- .../mapper/KafkaPublishTaskMapper.java | 2 +- .../tasks/kafka/KafkaProducerManagerTest.java | 2 +- .../tasks/kafka/KafkaPublishTaskTest.java | 2 +- .../mapper/KafkaPublishTaskMapperTest.java | 2 +- licenseheader.txt | 2 +- .../metrics/LoggingMetricsConfiguration.java | 2 +- .../metrics/MetricsRegistryConfiguration.java | 2 +- .../PrometheusMetricsConfiguration.java | 2 +- .../LoggingMetricsConfigurationTest.java | 2 +- .../PrometheusMetricsConfigurationTest.java | 2 +- .../mysql/config/MySQLConfiguration.java | 2 +- .../mysql/config/MySQLProperties.java | 2 +- .../conductor/mysql/dao/MySQLBaseDAO.java | 2 +- .../mysql/dao/MySQLExecutionDAO.java | 2 +- .../conductor/mysql/dao/MySQLMetadataDAO.java | 2 +- .../conductor/mysql/dao/MySQLQueueDAO.java | 2 +- .../conductor/mysql/util/ExecuteFunction.java | 2 +- .../conductor/mysql/util/LazyToString.java | 2 +- .../netflix/conductor/mysql/util/Query.java | 2 +- .../conductor/mysql/util/QueryFunction.java | 2 +- .../mysql/util/ResultSetHandler.java | 2 +- .../mysql/util/TransactionalFunction.java | 2 +- .../mysql/dao/MySQLExecutionDAOTest.java | 2 +- .../mysql/dao/MySQLMetadataDAOTest.java | 2 +- .../mysql/dao/MySQLQueueDAOTest.java | 2 +- .../grpc/mysql/MySQLGrpcEndToEndTest.java | 2 +- .../queue/stan/NATSAbstractQueue.java | 2 +- .../queue/stan/NATSObservableQueue.java | 2 +- .../queue/stan/NATSStreamObservableQueue.java | 2 +- .../queue/stan/config/NATSConfiguration.java | 2 +- .../stan/config/NATSEventQueueProvider.java | 2 +- .../stan/config/NATSStreamConfiguration.java | 2 +- .../config/NATSStreamEventQueueProvider.java | 2 +- .../stan/config/NATSStreamProperties.java | 2 +- .../queue/nats/JetStreamObservableQueue.java | 2 +- .../contribs/queue/nats/JsmMessage.java | 2 +- .../queue/nats/NATSAbstractQueue.java | 2 +- .../queue/nats/NATSObservableQueue.java | 2 +- .../contribs/queue/nats/NatsException.java | 2 +- .../nats/config/JetStreamConfiguration.java | 2 +- .../config/JetStreamEventQueueProvider.java | 2 +- .../nats/config/JetStreamProperties.java | 2 +- .../queue/nats/config/NATSConfiguration.java | 2 +- .../nats/config/NATSEventQueueProvider.java | 2 +- .../config/PostgresPayloadConfiguration.java | 2 +- .../config/PostgresPayloadProperties.java | 2 +- .../ExternalPostgresPayloadResource.java | 2 +- .../storage/PostgresPayloadStorage.java | 2 +- .../ExternalPostgresPayloadResourceTest.java | 2 +- .../storage/PostgresPayloadStorageTest.java | 2 +- .../storage/PostgresPayloadTestUtil.java | 2 +- .../config/PostgresConfiguration.java | 2 +- .../postgres/config/PostgresProperties.java | 2 +- .../postgres/dao/PostgresBaseDAO.java | 2 +- .../postgres/dao/PostgresExecutionDAO.java | 2 +- .../postgres/dao/PostgresIndexDAO.java | 2 +- .../postgres/dao/PostgresMetadataDAO.java | 2 +- .../postgres/dao/PostgresQueueDAO.java | 2 +- .../postgres/util/ExecuteFunction.java | 2 +- .../postgres/util/ExecutorsUtil.java | 2 +- .../conductor/postgres/util/LazyToString.java | 2 +- .../util/PostgresIndexQueryBuilder.java | 2 +- .../conductor/postgres/util/Query.java | 2 +- .../postgres/util/QueryFunction.java | 2 +- .../postgres/util/ResultSetHandler.java | 2 +- .../postgres/util/TransactionalFunction.java | 2 +- .../dao/PostgresExecutionDAOTest.java | 2 +- .../postgres/dao/PostgresIndexDAOTest.java | 2 +- .../postgres/dao/PostgresMetadataDAOTest.java | 2 +- .../postgres/dao/PostgresQueueDAOTest.java | 2 +- .../postgres/performance/PerformanceTest.java | 2 +- .../util/PostgresIndexQueryBuilderTest.java | 2 +- .../postgres/PostgresGrpcEndToEndTest.java | 2 +- .../RedisConcurrentExecutionLimitDAO.java | 2 +- ...ConcurrentExecutionLimitConfiguration.java | 2 +- ...disConcurrentExecutionLimitProperties.java | 2 +- ...edisConcurrentExecutionLimitDAOSpec.groovy | 2 +- .../config/RedisLockConfiguration.java | 2 +- .../redislock/config/RedisLockProperties.java | 2 +- .../conductor/redislock/lock/RedisLock.java | 2 +- .../conductor/redis/lock/RedisLockTest.java | 2 +- .../redis/config/AnyRedisCondition.java | 2 +- .../config/DynomiteClusterConfiguration.java | 2 +- .../config/InMemoryRedisConfiguration.java | 2 +- .../redis/config/JedisCommandsConfigurer.java | 2 +- .../config/RedisClusterConfiguration.java | 2 +- .../config/RedisCommonConfiguration.java | 2 +- .../redis/config/RedisProperties.java | 2 +- .../config/RedisSentinelConfiguration.java | 2 +- .../config/RedisStandaloneConfiguration.java | 2 +- .../conductor/redis/dao/BaseDynoDAO.java | 2 +- .../conductor/redis/dao/DynoQueueDAO.java | 2 +- .../redis/dao/RedisEventHandlerDAO.java | 2 +- .../redis/dao/RedisExecutionDAO.java | 2 +- .../conductor/redis/dao/RedisMetadataDAO.java | 2 +- .../conductor/redis/dao/RedisPollDataDAO.java | 2 +- .../redis/dao/RedisRateLimitingDAO.java | 2 +- .../dynoqueue/ConfigurationHostSupplier.java | 2 +- .../dynoqueue/LocalhostHostSupplier.java | 2 +- .../RedisQueuesShardingStrategyProvider.java | 2 +- .../conductor/redis/jedis/JedisCluster.java | 2 +- .../conductor/redis/jedis/JedisMock.java | 2 +- .../conductor/redis/jedis/JedisProxy.java | 2 +- .../conductor/redis/jedis/JedisSentinel.java | 2 +- .../redis/jedis/JedisStandalone.java | 2 +- ...disQueuesShardingStrategyProviderTest.java | 2 +- .../conductor/redis/dao/BaseDynoDAOTest.java | 2 +- .../conductor/redis/dao/DynoQueueDAOTest.java | 2 +- .../redis/dao/RedisEventHandlerDAOTest.java | 2 +- .../redis/dao/RedisExecutionDAOTest.java | 2 +- .../redis/dao/RedisMetadataDAOTest.java | 2 +- .../redis/dao/RedisPollDataDAOTest.java | 2 +- .../redis/dao/RedisRateLimitDAOTest.java | 2 +- .../jedis/ConfigurationHostSupplierTest.java | 2 +- .../redis/jedis/JedisClusterTest.java | 2 +- .../redis/jedis/JedisSentinelTest.java | 2 +- .../rest/config/RequestMappingConstants.java | 2 +- .../rest/config/RestConfiguration.java | 2 +- .../rest/controllers/AdminResource.java | 2 +- .../ApplicationExceptionMapper.java | 2 +- .../rest/controllers/EventResource.java | 2 +- .../rest/controllers/HealthCheckResource.java | 2 +- .../rest/controllers/MetadataResource.java | 2 +- .../rest/controllers/QueueAdminResource.java | 2 +- .../rest/controllers/TaskResource.java | 2 +- .../ValidationExceptionMapper.java | 2 +- .../controllers/WorkflowBulkResource.java | 2 +- .../rest/controllers/WorkflowResource.java | 2 +- .../rest/startup/KitchenSinkInitializer.java | 2 +- .../rest/controllers/AdminResourceTest.java | 2 +- .../rest/controllers/EventResourceTest.java | 2 +- .../controllers/MetadataResourceTest.java | 2 +- .../rest/controllers/TaskResourceTest.java | 2 +- .../controllers/WorkflowResourceTest.java | 2 +- .../java/com/netflix/conductor/Conductor.java | 2 +- .../config/ConductorObjectMapperTest.java | 2 +- .../AbstractResiliencySpecification.groovy | 2 +- .../test/base/AbstractSpecification.groovy | 2 +- .../test/integration/DecisionTaskSpec.groovy | 2 +- .../test/integration/DoWhileSpec.groovy | 2 +- .../integration/DynamicForkJoinSpec.groovy | 2 +- .../test/integration/EventTaskSpec.groovy | 2 +- .../test/integration/ExclusiveJoinSpec.groovy | 2 +- .../ExternalPayloadStorageSpec.groovy | 2 +- .../integration/FailureWorkflowSpec.groovy | 2 +- .../test/integration/ForkJoinSpec.groovy | 2 +- ...rchicalForkJoinSubworkflowRerunSpec.groovy | 2 +- ...hicalForkJoinSubworkflowRestartSpec.groovy | 2 +- ...rchicalForkJoinSubworkflowRetrySpec.groovy | 2 +- .../integration/JsonJQTransformSpec.groovy | 2 +- .../LambdaAndTerminateTaskSpec.groovy | 2 +- .../NestedForkJoinSubWorkflowSpec.groovy | 2 +- .../integration/SetVariableTaskSpec.groovy | 2 +- .../integration/SimpleWorkflowSpec.groovy | 2 +- .../test/integration/StartWorkflowSpec.groovy | 2 +- .../integration/SubWorkflowRerunSpec.groovy | 2 +- .../integration/SubWorkflowRestartSpec.groovy | 2 +- .../integration/SubWorkflowRetrySpec.groovy | 2 +- .../test/integration/SubWorkflowSpec.groovy | 2 +- .../test/integration/SwitchTaskSpec.groovy | 2 +- .../test/integration/SystemTaskSpec.groovy | 2 +- .../integration/TaskLimitsWorkflowSpec.groovy | 2 +- .../test/integration/TestWorkflowSpec.groovy | 2 +- .../test/integration/WaitTaskSpec.groovy | 2 +- .../WorkflowAndTaskConfigurationSpec.groovy | 2 +- .../resiliency/QueueResiliencySpec.groovy | 2 +- .../test/resiliency/TaskResiliencySpec.groovy | 2 +- .../test/util/WorkflowTestUtil.groovy | 2 +- .../netflix/conductor/ConductorTestApp.java | 2 +- .../integration/AbstractEndToEndTest.java | 2 +- .../grpc/AbstractGrpcEndToEndTest.java | 2 +- .../integration/grpc/GrpcEndToEndTest.java | 2 +- .../http/AbstractHttpEndToEndTest.java | 2 +- .../integration/http/HttpEndToEndTest.java | 2 +- .../utils/MockExternalPayloadStorage.java | 2 +- .../conductor/test/utils/UserTask.java | 2 +- .../netflix/conductor/ConductorTestApp.java | 2 +- .../config/TestObjectMapperConfiguration.java | 2 +- .../integration/AbstractEndToEndTest.java | 2 +- .../grpc/AbstractGrpcEndToEndTest.java | 2 +- ...rchivingWithTTLWorkflowStatusListener.java | 2 +- ...rchivingWorkflowListenerConfiguration.java | 2 +- .../ArchivingWorkflowListenerProperties.java | 2 +- .../ArchivingWorkflowStatusListener.java | 2 +- .../ConductorQueueStatusPublisher.java | 2 +- ...ctorQueueStatusPublisherConfiguration.java | 2 +- ...nductorQueueStatusPublisherProperties.java | 2 +- .../ArchivingWorkflowStatusListenerTest.java | 2 +- ...orkflowStatusPublisherIntegrationTest.java | 2 +- 670 files changed, 731 insertions(+), 685 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba24e0019..e94e3c16a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1,60 @@ -[Contributing](docs/docs/resources/contributing.md) \ No newline at end of file +# Contributing +Thanks for your interest in Conductor! +This guide helps to find the most efficient way to contribute, ask questions, and report issues. + +Code of conduct +----- + +Please review our [code of conduct](code-of-conduct.md). + +I have a question! +----- + +We have a dedicated [discussion forum](https://github.com/Netflix/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. +*Please do not create issues to ask questions.* + +I want to contribute! +------ + +We welcome Pull Requests and already had many outstanding community contributions! +Creating and reviewing Pull Requests take considerable time. This section helps you to set up a smooth Pull Request experience. + +The stable branch is [main](https://github.com/Netflix/conductor/tree/main). + +Please create pull requests for your contributions against [main](https://github.com/Netflix/conductor/tree/main) only. + +It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/Netflix/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. + +Also, consider that not every feature is a good fit for Conductor. A few things to consider are: + +* Is it increasing complexity for the user, or might it be confusing? +* Does it, in any way, break backward compatibility (this is seldom acceptable) +* Does it require new dependencies (this is rarely acceptable for core modules) +* Should the feature be opt-in or enabled by default. For integration with a new Queuing recipe or persistence module, a separate module which can be optionally enabled is the right choice. +* Should the feature be implemented in the main Conductor repository, or would it be better to set up a separate repository? Especially for integration with other systems, a separate repository is often the right choice because the life-cycle of it will be different. + +Of course, for more minor bug fixes and improvements, the process can be more light-weight. + +We'll try to be responsive to Pull Requests. Do keep in mind that because of the inherently distributed nature of open source projects, responses to a PR might take some time because of time zones, weekends, and other things we may be working on. + +I want to report an issue +----- + +If you found a bug, it is much appreciated if you create an issue. Please include clear instructions on how to reproduce the issue, or even better, include a test case on a branch. Make sure to come up with a descriptive title for the issue because this helps while organizing issues. + +I have a great idea for a new feature +---- +Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/Netflix/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. + +Once we have decided on a direction, it's time to summarize the idea by creating a new issue. + +## Code Style +We use [spotless](https://github.com/diffplug/spotless) to enforce consistent code style for the project, so make sure to run `gradlew spotlessApply` to fix any violations after code changes. + +## License + +By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/Netflix/conductor/blob/master/LICENSE + +All files are released with the Apache 2.0 license. + + diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java index 14609a3d5..d5839539a 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java index 305359979..c71850eb0 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java index 4b959530f..a4e23d232 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java index f5601733c..bbf3aab9c 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java index 6806fcad2..6e17ef8ef 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java index 4ca45975b..6d9d159a5 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/config/AMQPRetryPattern.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java index b4ba34d84..d0d3b93c5 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConfigurations.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java index 6cebcf10b..3abd619bf 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java index 410847396..db18dd9df 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/AMQPSettings.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java index e168bcac8..0b28ce273 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/ConnectionType.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java index 1be5e6337..192c49f25 100644 --- a/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java +++ b/amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/util/RetryType.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java index f99f464d0..2ca8a781a 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPEventQueueProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java index 84bf32b65..1b6139a4a 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java index 1776c381d..a9c5f26f0 100644 --- a/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java +++ b/amqp/src/test/java/com/netflix/conductor/contribs/queue/amqp/AMQPSettingsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/annotations-processor/src/example/java/com/example/Example.java b/annotations-processor/src/example/java/com/example/Example.java index f71f09c28..f55cfac6e 100644 --- a/annotations-processor/src/example/java/com/example/Example.java +++ b/annotations-processor/src/example/java/com/example/Example.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java index f4c7aef1a..3794a7ca0 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/AbstractMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java index 5916f7f5e..d1b4564bd 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Enum.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java index 45fe055ad..ba6b1122b 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/Message.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java index 58624f874..e562c0f88 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java index 836bb4b23..a2716eb3f 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGen.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java index 0819555e9..d161d6ffb 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java index 83999171c..6a38fd551 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/AbstractType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java index a04b7803d..93279d515 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ExternMessageType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java index 75c007174..adf8f4d8f 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/GenericType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java index e56fdf073..50bbf1c7e 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ListType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java index 88eed8db3..bdcb5bfe0 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MapType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java index 3cb8938bc..724817af8 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/MessageType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java index 1e04438f9..afefc788e 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/ScalarType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java index d21129858..7e997098f 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/TypeMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java index ac0567c7b..409e47dfa 100644 --- a/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java +++ b/annotations-processor/src/main/java/com/netflix/conductor/annotationsprocessor/protogen/types/WrappedType.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java b/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java index 7e22160d6..09f637215 100644 --- a/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java +++ b/annotations-processor/src/test/java/com/netflix/conductor/annotationsprocessor/protogen/ProtoGenTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java index 99873a8aa..c07e679f7 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoEnum.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java index c238a9358..a61bb5ea1 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoField.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java index c98b55e16..45fa884f9 100644 --- a/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java +++ b/annotations/src/main/java/com/netflix/conductor/annotations/protogen/ProtoMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java index c9b95f6df..b14d79395 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java index 6630842db..9c41b4a10 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/config/S3Properties.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java b/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java index 75492872a..19ac68d27 100644 --- a/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java +++ b/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java index f3de6fd35..1f4165423 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java index 8570e538a..fbe40eea9 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java index 3b76cd016..425012086 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/config/SQSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java index 71f2fde6f..5f3e4173b 100644 --- a/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java +++ b/awssqs-event-queue/src/main/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java index 5a4b93d4e..1607acf44 100644 --- a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java +++ b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/DefaultEventQueueProcessorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java index c336b4214..39acc6f54 100644 --- a/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java +++ b/awssqs-event-queue/src/test/java/com/netflix/conductor/sqs/eventqueue/SQSObservableQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java index e73935d5d..49c0a2f68 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java index bd52006c6..3932bd677 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/config/AzureBlobProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java index 7d5d58756..6d1ead96d 100644 --- a/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java +++ b/azureblob-storage/src/main/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java index f47857a13..2935f609d 100644 --- a/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java +++ b/azureblob-storage/src/test/java/com/netflix/conductor/azureblob/storage/AzureBlobPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java index 4cd97652c..f415aa55f 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java index 69c481d0d..37ca274de 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/CassandraProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java index cad60b79d..4973e811e 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java index c6eec0d99..7ccdeb96a 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CacheableMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java index 25b2aec52..6255ce47e 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/config/cache/CachingConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java index a954cf004..e6fb4b27a 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java index 540fa02a1..db6e1fadf 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java index 6b424981f..6ced38b8d 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java index 07c649594..9c6de6438 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java index 4e0d4f040..8e7e4d1e3 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/dao/CassandraPollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java index 284db18e2..1d815ae62 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java index 8f2b8cdb3..63e6b4a06 100644 --- a/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java +++ b/cassandra-persistence/src/main/java/com/netflix/conductor/cassandra/util/Statements.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy index ae4b1ec39..e0d437d7b 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraEventHandlerDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy index 01c57923c..13e79e238 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraExecutionDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy index d81d2f317..498435cdc 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraMetadataDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy index 8f39b69a8..935788e89 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/dao/CassandraSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy index ccb1d1713..8517e7e4a 100644 --- a/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy +++ b/cassandra-persistence/src/test/groovy/com/netflix/conductor/cassandra/util/StatementsSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java index e5c46604a..87b13cbfe 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java index f4c827efc..0219edbf1 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java index 10bf1458d..dd6983f33 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java b/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java index a89f6b90e..231bf92e4 100644 --- a/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java +++ b/client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java b/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java index 396350d03..4bca46e97 100644 --- a/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java +++ b/client-spring/src/test/java/com/netflix/conductor/client/spring/ExampleClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java b/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java index e28930b60..8f1fb1a44 100644 --- a/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java +++ b/client-spring/src/test/java/com/netflix/conductor/client/spring/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java b/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java index b1e9472ed..c4304c2d5 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/PollingSemaphore.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java b/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java index 5ce3d119f..b0cb46c53 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/TaskPollExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java index 1208427a6..3f740e6e4 100644 --- a/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java +++ b/client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java b/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java index 27551029b..bb5a28926 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java +++ b/client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Conductor authors. + * Copyright 2018 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java b/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java index 1b40e2fd6..13e7f3247 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java +++ b/client/src/main/java/com/netflix/conductor/client/config/DefaultConductorClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java b/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java index b3736a2eb..dcde89a37 100644 --- a/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java +++ b/client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java b/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java index a72ed8806..a2af3a06b 100644 --- a/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java +++ b/client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java b/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java index fea6cb7a5..5326abe4d 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java +++ b/client/src/main/java/com/netflix/conductor/client/http/ClientBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java b/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java index 5bfe06b1e..164cb579b 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java +++ b/client/src/main/java/com/netflix/conductor/client/http/ClientRequestHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/EventClient.java b/client/src/main/java/com/netflix/conductor/client/http/EventClient.java index 0d0ddddc7..1782d0177 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/EventClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/EventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java b/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java index 915ca55d0..2db8df237 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java b/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java index bd2f791df..668e50047 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java +++ b/client/src/main/java/com/netflix/conductor/client/http/PayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java b/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java index 1654fcc83..35d48b2e3 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/TaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java b/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java index b8c5083b0..14deabff3 100644 --- a/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java +++ b/client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java b/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java index 9439838ee..d80349ff9 100644 --- a/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java +++ b/client/src/main/java/com/netflix/conductor/client/telemetry/MetricsContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/main/java/com/netflix/conductor/client/worker/Worker.java b/client/src/main/java/com/netflix/conductor/client/worker/Worker.java index b3a213078..5b0ecc0b1 100644 --- a/client/src/main/java/com/netflix/conductor/client/worker/Worker.java +++ b/client/src/main/java/com/netflix/conductor/client/worker/Worker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy index abf5719d7..c804f785c 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy index bb6599271..bb121c56d 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy index 4d3efcddb..13790d3fc 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy index db9daf5b7..2caba4490 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy b/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy index 73b81884f..b4f30aeb1 100644 --- a/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy +++ b/client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java b/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java index c7fa71062..7bc7e7c3a 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java b/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java index a9375d1c1..60855bdae 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java b/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java index c68c2ff48..cbd9df8a9 100644 --- a/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java +++ b/client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java b/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java index 365ebad9b..398738c59 100644 --- a/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java +++ b/client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/sample/Main.java b/client/src/test/java/com/netflix/conductor/client/sample/Main.java index b91cc4582..7a474b146 100644 --- a/client/src/test/java/com/netflix/conductor/client/sample/Main.java +++ b/client/src/test/java/com/netflix/conductor/client/sample/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java b/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java index 927e1b9fc..6073c3f90 100644 --- a/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java +++ b/client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java b/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java index 6e1689c1a..c2afa03f3 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java index d8db7646f..cd73b1af4 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java index 5a1ccb693..84f876a1a 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java b/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java index 4f1cf70d4..e67f83658 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java b/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java index 2eed2d85c..f01d3f921 100644 --- a/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java +++ b/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java b/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java index c084f4dc2..32f7bcb76 100644 --- a/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java +++ b/client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java index 0ddbacbbb..fc4068706 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java index 999028732..7d1d14106 100644 --- a/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java +++ b/common-persistence/src/test/java/com/netflix/conductor/dao/TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java index 326aa5938..40b781eab 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperBuilderConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java index 8828f4d0e..9d698cb1b 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java index e6d3ce1d9..51ebfc8cf 100644 --- a/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java +++ b/common/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java index cdb270e6a..b3482c979 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/NoSemiColonConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java index 434b581c0..297d61442 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/OwnerEmailMandatoryConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java index 758ab88e7..3d325f577 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskReferenceNameUniqueConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java index d360a9536..a9e6576f0 100644 --- a/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java +++ b/common/src/main/java/com/netflix/conductor/common/constraints/TaskTimeoutConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java b/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java index 56eca4467..b109e8ea9 100644 --- a/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java +++ b/common/src/main/java/com/netflix/conductor/common/jackson/JsonProtoModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java b/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java index c83dc925d..fcdfdf9fa 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/Auditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java index 87e33f79a..7fec07255 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/BaseDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java b/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java index 9065b1034..dfcc77571 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/acl/Permission.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java index 3b1f4c36d..fd5310ac5 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java index 947d76146..0321c85b7 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java index 534721386..f094fb20a 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java index 095bd00e9..90fd0ee4a 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java index 45b9afed2..f6d5964d7 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java index e044af1f0..a04eb1257 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java index 1414f9e9a..704c9fa70 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java index de22c0340..902027b02 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java index 3f2fe73cc..05c5dfbfb 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java index b3f57cf5d..ca5292b51 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java index 4c7850ac7..82d802109 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java index fbbef2153..42371c943 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java index 1974046f6..fc8f83af7 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java index fa108ce40..d2fbb6f3b 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java index bc591edda..02c4d0149 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java index e00fc00fb..6c8d1f36e 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java index 344249ab8..94c9c00e0 100644 --- a/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java +++ b/common/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java b/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java index 112333489..b4133b5bf 100644 --- a/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java +++ b/common/src/main/java/com/netflix/conductor/common/model/BulkResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java b/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java index 09c479a18..d94b35837 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java +++ b/common/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java b/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java index 73fe30bec..43057d985 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java +++ b/common/src/main/java/com/netflix/conductor/common/run/SearchResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java b/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java index 45c3125ec..32e3b0f71 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/run/TaskSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java index d988c8ec1..26a8b5598 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/Workflow.java +++ b/common/src/main/java/com/netflix/conductor/common/run/Workflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java b/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java index 82498a0fa..9be8d7df1 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java +++ b/common/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java b/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java index eab019f64..e41394688 100644 --- a/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java +++ b/common/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java b/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java index 0d1cb8e67..249db9dcd 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java b/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java index 9120d6235..7cb8ab463 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java b/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java index c860e487d..f246b84b7 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java index 12e64bffb..85c0e2010 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java b/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java index 65191d4ff..6ba1f11ba 100644 --- a/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java +++ b/common/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java b/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java index e76fa2260..f9183c928 100644 --- a/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java +++ b/common/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java b/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java index ada55929e..82d63f520 100644 --- a/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java +++ b/common/src/main/java/com/netflix/conductor/common/validation/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java index 66e07e565..b835693f9 100644 --- a/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java +++ b/common/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java index 1a6681f04..db23c69dd 100644 --- a/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java +++ b/common/src/test/java/com/netflix/conductor/common/events/EventHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java b/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java index de72cad0a..09c7077c1 100644 --- a/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java +++ b/common/src/test/java/com/netflix/conductor/common/run/TaskSummaryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java index 0991b9033..f370138fc 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskDefTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java index 9ae897ae1..54888202d 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java b/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java index a6c9941ff..255108170 100644 --- a/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java +++ b/common/src/test/java/com/netflix/conductor/common/tasks/TaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java b/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java index 1ce01bbc6..543035054 100644 --- a/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java +++ b/common/src/test/java/com/netflix/conductor/common/utils/ConstraintParamUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java b/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java index 730f0c222..435434f28 100644 --- a/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java +++ b/common/src/test/java/com/netflix/conductor/common/utils/SummaryUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java index 7d1b50ae3..d32afc5f6 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/SubWorkflowParamsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java index b37e6af2c..d08acdc77 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowDefValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java index 3192f62fb..12a8aa399 100644 --- a/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java +++ b/common/src/test/java/com/netflix/conductor/common/workflow/WorkflowTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/Audit.java b/core/src/main/java/com/netflix/conductor/annotations/Audit.java index dc779d24a..3e55d7804 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/Audit.java +++ b/core/src/main/java/com/netflix/conductor/annotations/Audit.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/Trace.java b/core/src/main/java/com/netflix/conductor/annotations/Trace.java index d750c47e1..cf2b9ec5e 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/Trace.java +++ b/core/src/main/java/com/netflix/conductor/annotations/Trace.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java b/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java index 7a7bae16f..fb1ae76b6 100644 --- a/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java +++ b/core/src/main/java/com/netflix/conductor/annotations/VisibleForTesting.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java b/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java index 7dfd74dfc..153cea1fb 100644 --- a/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java +++ b/core/src/main/java/com/netflix/conductor/core/LifecycleAwareComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java b/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java index 6575cf517..559506dad 100644 --- a/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java +++ b/core/src/main/java/com/netflix/conductor/core/WorkflowContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java b/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java index 17a9d1a45..c0e8de9ee 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/config/ConductorCoreConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java b/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java index 07b4eaf0b..80b127372 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java +++ b/core/src/main/java/com/netflix/conductor/core/config/ConductorProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java b/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java index c24e4ef46..b63366649 100644 --- a/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/config/SchedulerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java index d2ffbc71a..c5cdff08c 100644 --- a/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java +++ b/core/src/main/java/com/netflix/conductor/core/dal/ExecutionDAOFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java b/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java index 3e5748c56..c6d7085ef 100644 --- a/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java +++ b/core/src/main/java/com/netflix/conductor/core/event/WorkflowCreationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java b/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java index 38b21b797..267224b56 100644 --- a/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java +++ b/core/src/main/java/com/netflix/conductor/core/event/WorkflowEvaluationEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java index 90da4b775..83e60fbb8 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/ActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java index 2f45d6848..dbdbf6b15 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java index e9f492347..a83755069 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java +++ b/core/src/main/java/com/netflix/conductor/core/events/DefaultEventQueueManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java index b31fa737b..7580af79c 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueueManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java index c291f27fd..22e3cecc3 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java b/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java index 306f1c8c5..9cbe08a46 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java +++ b/core/src/main/java/com/netflix/conductor/core/events/EventQueues.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java b/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java index 5543bb60b..1cd5e4a89 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java index 2c292160a..a91bcad43 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/SimpleActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java index c82fc2e88..478a8d7b2 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java index 142290f3d..e4689423d 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ConductorObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java b/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java index 22b2fceab..ff49a173f 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/DefaultEventQueueProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java b/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java index b97db4420..edefe53af 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/Message.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java b/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java index d29b1ada2..ac5098ea7 100644 --- a/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java +++ b/core/src/main/java/com/netflix/conductor/core/events/queue/ObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java b/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java index 54f33bd6e..21cbd6071 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/ConflictException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java b/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java index 318607dc3..0bf93d14d 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/NonTransientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java b/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java index cc8ab0fb5..03f4d1d5c 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/NotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java b/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java index e244e6733..aa8ec4a50 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/TerminateWorkflowException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java b/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java index aeb3949bf..c6e536b15 100644 --- a/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java +++ b/core/src/main/java/com/netflix/conductor/core/exception/TransientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java index eb14ce93a..8751f1582 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/AsyncSystemTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java index c0985ce44..6d7a42e70 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java b/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java index 86e83355b..f24ea2771 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/StartWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java index 01e1699ab..84ee5e001 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java index c906a4ec4..13bce2b24 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/Evaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java index a4911d186..4a22bdbe9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/JavascriptEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java index 72a8e5ecd..94c34b0d0 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java index 4254d7c76..4ac394de8 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java index 303a51ae2..85ff93e81 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java index f6e95852b..d94af8f0b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java index f498aa235..96b9d582f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/EventTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java index 48c1c03d7..587460cf9 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ExclusiveJoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java index 15f9ba36b..e0acd5b9f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java index 089616efc..aefabb0a1 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java index a56cf8a6a..837e49549 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java index 4eda67abd..4484337f7 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java index 56b4274a3..87eac7259 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java index 6fb8e8ae9..6ae3adb1e 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java index ad025d6d1..21828e3b7 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java index bb9af7761..862504666 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java index 437a1252f..37294faa2 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java index 152e3f28b..17a53dba4 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java index b1d0ef5f9..e12caf47e 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java index 20f313e1a..0e1368cd1 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java index d78691b49..5a377750f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/StartWorkflowTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java index 466f8194f..ab7d9c291 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java index 4b4360004..2401b5c2c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java index 7f9610275..9f7d83e4c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java index 1a96aba95..44f996268 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TaskMapperContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java index be4b2627c..886f20420 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java index 80bbd6686..ba8f314d2 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java b/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java index 6b979c6db..fc58d892e 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java index 26f041c63..90941edf2 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Decision.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java index e0dc0ea9c..f273a8512 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/DoWhile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java index a71e59f7d..9674efe07 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java index bb868ad40..fc1325e5e 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExclusiveJoin.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java index fc5aeb149..0f1a996e7 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/ExecutionConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java index 78f88e782..6d7ddf74f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Fork.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java index 677613152..15528652c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Human.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java index 67ac5f2a0..5641fb441 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Inline.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java index 7ffa15a68..9ac9b4bab 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java index 3d032cfe3..a0ad5a96f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Join.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java index a9fcac9f9..8df79263e 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Lambda.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java index 34a538607..49e6083cd 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Noop.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java index 79ed0e8f4..0c8327958 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SetVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java index 8cfe160dc..b9cb01e0c 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/StartWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java index 0d2a9c789..6e29dd64d 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java index 57d65f239..bcbd00417 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Switch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java index dffef6cb3..7b9cf743b 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java index 066755d0f..d13334708 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java index 1d57abd3b..c192a9639 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/SystemTaskWorkerCoordinator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java index c38fd87c5..23eed4196 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Terminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java index 39d7487e3..2e1a1cc2f 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/Wait.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java b/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java index 4f898773f..9cc17b9e4 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java index d57085e84..2472c4b38 100644 --- a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java +++ b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java index bcb488c04..5de6a6e51 100644 --- a/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/index/NoopIndexDAOConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java index 3823e3b2a..0fc6d61d4 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java index 99a811f3b..a53d05243 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/TaskStatusListenerStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java index 586681313..18dbfeb29 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java index e4789ff7c..63fe09070 100644 --- a/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java +++ b/core/src/main/java/com/netflix/conductor/core/listener/WorkflowStatusListenerStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java b/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java index 63aee4fc9..31e4abaca 100644 --- a/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java +++ b/core/src/main/java/com/netflix/conductor/core/metadata/MetadataMapperService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java b/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java index 3e3632aee..3ac92eb53 100644 --- a/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java +++ b/core/src/main/java/com/netflix/conductor/core/operation/StartWorkflowOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java b/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java index 963391478..73a000042 100644 --- a/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java +++ b/core/src/main/java/com/netflix/conductor/core/operation/WorkflowOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java index 972f28da8..a95d1920f 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowReconciler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java index ca2e0d8ac..2266f604f 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowRepairService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java index 01e1eb606..a83ea4d39 100644 --- a/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java +++ b/core/src/main/java/com/netflix/conductor/core/reconciliation/WorkflowSweeper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java b/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java index 4566860f0..a083405eb 100644 --- a/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java +++ b/core/src/main/java/com/netflix/conductor/core/storage/DummyPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/Lock.java b/core/src/main/java/com/netflix/conductor/core/sync/Lock.java index 02ccd48dd..b219d41f2 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/Lock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/Lock.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java index c2255d834..97408257b 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java index ad90d327f..790fda65e 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/local/LocalOnlyLockConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java b/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java index 55de2cfee..e372b26f7 100644 --- a/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java +++ b/core/src/main/java/com/netflix/conductor/core/sync/noop/NoopLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java index 52cff75e8..7da8e43e8 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/DateTimeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java index 771baffc1..e9ff71f1a 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java b/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java index 488373173..0f9060416 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/IDGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java index a654d299c..2ec74e5a3 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/JsonUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java index 9a8b2d8a9..199dbac47 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java index 7dafb2617..0fa463d93 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/QueueUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java b/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java index 65b208eea..df4d23120 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/SemaphoreUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/core/utils/Utils.java b/core/src/main/java/com/netflix/conductor/core/utils/Utils.java index b8be0b0e5..2464d996b 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/Utils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java b/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java index d083d5545..5ca70c6b0 100644 --- a/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/ConcurrentExecutionLimitDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java b/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java index b7eb1f3e0..5d4812190 100644 --- a/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/EventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java b/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java index 2990afa8e..b94ffaf59 100644 --- a/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/ExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java b/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java index bb51e8309..ed3ada875 100644 --- a/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/IndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java b/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java index 6bb6034a2..712096cd3 100644 --- a/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/MetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java b/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java index ce8ee0422..7a1230d6c 100644 --- a/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/PollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java b/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java index ba190e3ac..eccfe07b0 100644 --- a/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/QueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java b/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java index 6c2231d24..65ad25fa0 100644 --- a/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java +++ b/core/src/main/java/com/netflix/conductor/dao/RateLimitingDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/metrics/Monitors.java b/core/src/main/java/com/netflix/conductor/metrics/Monitors.java index 946e1553c..cbe529ae4 100644 --- a/core/src/main/java/com/netflix/conductor/metrics/Monitors.java +++ b/core/src/main/java/com/netflix/conductor/metrics/Monitors.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java b/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java index 422c3e0ca..e1e8fc637 100644 --- a/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java +++ b/core/src/main/java/com/netflix/conductor/metrics/WorkflowMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/model/TaskModel.java b/core/src/main/java/com/netflix/conductor/model/TaskModel.java index eebbf8cb3..59875ac43 100644 --- a/core/src/main/java/com/netflix/conductor/model/TaskModel.java +++ b/core/src/main/java/com/netflix/conductor/model/TaskModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java b/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java index 995c5ee83..c0bfffccd 100644 --- a/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java +++ b/core/src/main/java/com/netflix/conductor/model/WorkflowModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/AdminService.java b/core/src/main/java/com/netflix/conductor/service/AdminService.java index 2ebffc954..973a3edc8 100644 --- a/core/src/main/java/com/netflix/conductor/service/AdminService.java +++ b/core/src/main/java/com/netflix/conductor/service/AdminService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java index 88b980633..8c11dd94b 100644 --- a/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/AdminServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/EventService.java b/core/src/main/java/com/netflix/conductor/service/EventService.java index 36ababc5e..3d42a872b 100644 --- a/core/src/main/java/com/netflix/conductor/service/EventService.java +++ b/core/src/main/java/com/netflix/conductor/service/EventService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java index 852a5bbb1..579986e11 100644 --- a/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/EventServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java b/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java index 63a530fac..b7539ca4d 100644 --- a/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java +++ b/core/src/main/java/com/netflix/conductor/service/ExecutionLockService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java index fcb23b50b..a33f1814b 100644 --- a/core/src/main/java/com/netflix/conductor/service/ExecutionService.java +++ b/core/src/main/java/com/netflix/conductor/service/ExecutionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/MetadataService.java b/core/src/main/java/com/netflix/conductor/service/MetadataService.java index e79fe6b69..5edd42ef5 100644 --- a/core/src/main/java/com/netflix/conductor/service/MetadataService.java +++ b/core/src/main/java/com/netflix/conductor/service/MetadataService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java index 5c9a6357a..d48b95a42 100644 --- a/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/MetadataServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/TaskService.java b/core/src/main/java/com/netflix/conductor/service/TaskService.java index f435fafef..9d29600ac 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskService.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java index ede5cd0fd..3749e7dfd 100644 --- a/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/TaskServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java index d49f05395..de6bd4292 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java index 1461b5a42..5e05ae453 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java index 65e63c627..2253cad54 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java b/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java index 255390d46..93777afd5 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java b/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java index 1c58ee535..601c0aba8 100644 --- a/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java +++ b/core/src/main/java/com/netflix/conductor/service/WorkflowTestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java b/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java index 5916e1275..6bf5ed000 100644 --- a/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java +++ b/core/src/main/java/com/netflix/conductor/validations/ValidationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java index 8588cb721..290d8692b 100644 --- a/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java +++ b/core/src/main/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraint.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy index 609e58012..69a68d7fb 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/AsyncSystemTaskExecutorTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy index 421f3f5a7..8c6f77539 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/DoWhileSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy index 6696b1449..e0ba3ddb1 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/EventSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy index 3652e4eec..9daeea0d9 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/IsolatedTaskQueueProducerSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy index b78253677..1ae1a64db 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/execution/tasks/StartWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy b/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy index e0a0e93fb..187879586 100644 --- a/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/core/operation/StartWorkflowOperationSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy b/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy index 4a2d1b85b..8938f7411 100644 --- a/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/model/TaskModelSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy b/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy index b7e5ddabb..593f6b608 100644 --- a/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy +++ b/core/src/test/groovy/com/netflix/conductor/model/WorkflowModelSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/TestUtils.java b/core/src/test/java/com/netflix/conductor/TestUtils.java index 59b727b57..2229d3da3 100644 --- a/core/src/test/java/com/netflix/conductor/TestUtils.java +++ b/core/src/test/java/com/netflix/conductor/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java b/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java index 0cd66be9c..7f9928ffb 100644 --- a/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java +++ b/core/src/test/java/com/netflix/conductor/core/dal/ExecutionDAOFacadeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java b/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java index 7bbc547ca..79d4706cb 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java +++ b/core/src/test/java/com/netflix/conductor/core/events/MockObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java b/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java index 574929c41..3900a4501 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java +++ b/core/src/test/java/com/netflix/conductor/core/events/MockQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java b/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java index 207d6a826..234260899 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestDefaultEventProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java b/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java index acf2bd77b..313c02bc3 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestScriptEval.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java b/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java index 18769f879..cd9e62290 100644 --- a/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java +++ b/core/src/test/java/com/netflix/conductor/core/events/TestSimpleActionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java index 986625d17..d06029020 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderOutcomes.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java index 85f141882..7e78aee4e 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestDeciderService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java index 61c107026..7df603727 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java index cb8f07868..b974c5c95 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/TestWorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java b/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java index 972968c62..2f4be6c17 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/WorkflowSystemTaskStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java index 893a540db..36c115121 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DecisionTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java index 8d06db695..1855693aa 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DoWhileTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java index 52fbc1703..936d262f1 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/DynamicTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java index 100181064..0a7db93ad 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/EventTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java index 192b78e6c..83d5bac06 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinDynamicTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java index aa1415cb3..e3d2c2f70 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/ForkJoinTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java index 829e10352..975a7fe70 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HTTPTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java index e389c373c..d42cd688b 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/HumanTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java index 6a7ad4151..42bc050e1 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/InlineTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java index 25cf9c3dd..ba6d00c0b 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JoinTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java index d1f17c331..947594459 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/JsonJQTransformTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java index 4386c1e5d..052479fc4 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java index 106635960..4ec34f59d 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/LambdaTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java index aa1c4dbaa..13f67236b 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/NoopTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java index ee43ff652..848c67ace 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SetVariableTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java index 98c1d41b0..bebf2e249 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SimpleTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java index 61c6cde13..aac93a104 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SubWorkflowTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java index 6a997fede..7f76ced6e 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/SwitchTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java index 9366b5870..b7b4fad3e 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/TerminateTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java index 6623997b6..b465b1fa6 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/UserDefinedTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java b/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java index c025afd35..ddc813f59 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/mapper/WaitTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java index 405f224ba..1e0d5d79c 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/EventQueueResolutionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java index b9287266c..51cfec7c0 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/InlineTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java index ecedc54ea..0d9b03017 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestLambda.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java index 52d63fa65..d6f34dd59 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestNoop.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java index 0f827fa55..2d5fcccd8 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java index bf7d7e4a7..bdee32b7c 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java index 26286aab5..9e04e8d70 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestSystemTaskWorkerCoordinator.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java index 347d73451..183153889 100644 --- a/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java +++ b/core/src/test/java/com/netflix/conductor/core/execution/tasks/TestTerminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java index 0ae2580f2..1999dfbfa 100644 --- a/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/core/metadata/MetadataMapperServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java index 68639f310..54f17fec4 100644 --- a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java +++ b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowRepairService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java index d5c9ca594..5accfb580 100644 --- a/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java +++ b/core/src/test/java/com/netflix/conductor/core/reconciliation/TestWorkflowSweeper.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java b/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java index 466436597..30a42c8ad 100644 --- a/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java +++ b/core/src/test/java/com/netflix/conductor/core/storage/DummyPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java b/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java index 96f1c4379..a9b7dbba2 100644 --- a/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java +++ b/core/src/test/java/com/netflix/conductor/core/sync/local/LocalOnlyLockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java index 7f375fe13..dbd4557bc 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java index 05e4e16ac..18ca51135 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/JsonUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java index 57c8dd1b7..719972234 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java index a3cad357a..277625a39 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/QueueUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java b/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java index 30dfc8e91..9afb56d96 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/SemaphoreUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java b/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java index a61e3a946..7fcbede8f 100644 --- a/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java +++ b/core/src/test/java/com/netflix/conductor/dao/ExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java b/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java index 925690e3d..856ce6c41 100644 --- a/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java +++ b/core/src/test/java/com/netflix/conductor/dao/PollDataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java b/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java index dc8cd7ad2..15917c088 100644 --- a/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java +++ b/core/src/test/java/com/netflix/conductor/metrics/WorkflowMonitorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java index 5f0b75448..ce96ce874 100644 --- a/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/EventServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java b/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java index 33f7e3a51..9c7a631e3 100644 --- a/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/ExecutionServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java index f6859dcdb..1fa3f1990 100644 --- a/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/MetadataServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java index 6f8b3ecf8..ab06440f7 100644 --- a/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/TaskServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java index 25ea16088..27c23b992 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java index 566a710c7..226d51e03 100644 --- a/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java +++ b/core/src/test/java/com/netflix/conductor/service/WorkflowServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java index 8406deb05..655b4d75e 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowDefConstraintTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java index 932db00b2..eb1d88dd3 100644 --- a/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java +++ b/core/src/test/java/com/netflix/conductor/validations/WorkflowTaskTypeConstraintTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/docs/docs/resources/contributing.md b/docs/docs/resources/contributing.md index 89af84735..4588a55a9 100644 --- a/docs/docs/resources/contributing.md +++ b/docs/docs/resources/contributing.md @@ -55,19 +55,6 @@ We use [spotless](https://github.com/diffplug/spotless) to enforce consistent co By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/Netflix/conductor/blob/master/LICENSE -All files are released with the Apache 2.0 license, and the following license header will be automatically added to your new file if none present: - -``` -/** - * Copyright $YEAR Netflix, Inc. - * - * 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. - */ -``` +All files are released with the Apache 2.0 license. + + diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java index b0e08f517..e36f38dee 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java index 43dad6b5e..010d17565 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java index 60f335599..4b6e65922 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/config/ElasticSearchV7Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java index fb19cc6ca..299402582 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestBuilderWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java index d21d372ff..65d857cb9 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/BulkRequestWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java index c4227d2b4..b202a42ab 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java index f74817a9e..8af6d8135 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDAOV7.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java index ea0377b73..365643f46 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/Expression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java index 53d7d4045..6c5641457 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/FilterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java index 16b9e86c2..051741c65 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/GroupedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java index 726407a80..26b1642b9 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/NameValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java index f8d20c731..4bb8e73e8 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java index 605a09c40..b630b8a37 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/BooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java index 3c232f142..e54f020f8 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java index aafa3ad9f..32fa2b117 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java index fda089f06..5fece418d 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/FunctionThrowingException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java index eae278c5f..aa9394576 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ListConst.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java index f54d78339..5c9944528 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Name.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java index 103096072..37f68267c 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/ParserException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java index 94fc724e2..36104de47 100644 --- a/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java +++ b/es7-persistence/src/main/java/com/netflix/conductor/es7/dao/query/parser/internal/Range.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java index 38f32c627..f3389dbfe 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchRestDaoBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java index 8d20ecfe1..6183b9182 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/ElasticSearchTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java index beedf07c5..2709e3913 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestBulkRequestBuilderWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java index d214cdb8e..4bccf1deb 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java index 1a75ccf88..373339c8e 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/index/TestElasticSearchRestDAOV7Batch.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java index 5b543e7f7..b8d580b0c 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java index 23f4ff625..d114ec0e4 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/TestGroupedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java index 6d5b5996a..91e3583da 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/AbstractParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java index 69267162d..d2b6f38c5 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestBooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java index c1e3f9b9e..585efd2c5 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java index 148452127..28871d4ed 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java index 125c99f6c..b7d2a7a4a 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/dao/query/parser/internal/TestName.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java index 0868d4c75..729336ed5 100644 --- a/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java +++ b/es7-persistence/src/test/java/com/netflix/conductor/es7/utils/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java index 5d182d1ef..90b61128c 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/ClientBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java index f3811c83c..5f11ba94b 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/EventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java index 2f059c10b..f05f5ca8f 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/MetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java index 6309e84ab..6004db595 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/TaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java index a32385e22..557ace652 100644 --- a/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java +++ b/grpc-client/src/main/java/com/netflix/conductor/client/grpc/WorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java index cbcb61b9f..44b890685 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/EventClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java index 7e00a2795..367318abe 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/TaskClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java index 5762193a3..289c526c9 100644 --- a/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java +++ b/grpc-client/src/test/java/com/netflix/conductor/client/grpc/WorkflowClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java index 83b3181bb..e599f8fb0 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java index c631cd831..ad4dc646c 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GRPCServerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java index edf4f4291..77be5a974 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/GrpcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java index a82be1ae2..497454796 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/EventServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java index a91dabf9b..95a910243 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/GRPCHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java index 9fad15dd7..e2e947e1d 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/HealthServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java index 89c199496..9aad0b974 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/MetadataServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java index 6190847e8..33bc7560a 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/TaskServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java index 8abc17674..df60fb662 100644 --- a/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java +++ b/grpc-server/src/main/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java index b48d827f9..905a98c80 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/HealthServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java index 2d32fd82c..1bfd8256f 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/TaskServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java index 0bc6f4403..6be23634b 100644 --- a/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java +++ b/grpc-server/src/test/java/com/netflix/conductor/grpc/server/service/WorkflowServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java index 7c54edb1f..d85fa2484 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/HttpTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java index e559da67f..0efae0591 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java index 38176eb59..6744c0b66 100644 --- a/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java +++ b/http-task/src/main/java/com/netflix/conductor/tasks/http/providers/RestTemplateProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java index 7409d0501..5a26fd59a 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/HttpTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java index 812fb0fd4..7dba45210 100644 --- a/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java +++ b/http-task/src/test/java/com/netflix/conductor/tasks/http/providers/DefaultRestTemplateProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java index fdcbef968..7d8a4829f 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java index 975b855ee..ba621a564 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/Shipment.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java index e62eb597b..d71f58436 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java index 4bd0acf2a..9698f1e08 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkers.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java index 1f8f56d77..809dec6b3 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/ShipmentWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java index 1744388cc..0c8324a93 100644 --- a/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java +++ b/java-sdk/example/java/com/netflix/conductor/sdk/example/shipment/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java index a8bd0b873..014ab1d80 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java index bf0e1978f..0d60c9f4d 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java index 6a8128cdd..398d5c29f 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java index 6b507ea98..d3bc031ca 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java index 7a2665396..f795ab5f9 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java index 7202b8dea..2e90f7aa6 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java index 428f29f1e..6d616542e 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java index ecea039e5..9ad9b3593 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java index 8381ee68a..e3ae4f182 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java index e3650ac9f..d715f82f9 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java index de86607ff..4911a635d 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java index ca61ae5a3..239c935ac 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java index 550d03b65..06c4ff9fa 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java index bc1133831..9121d539d 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java index f4cd6e279..7260a0586 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java index fd9791ec1..2996d8582 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java index 743bbc1c3..c4260154a 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java index d6afcf182..7cea277c3 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java index 5f85e3fc6..3707cfba5 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java index ae091168f..bcff758ff 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java index 1f5fae6f6..28618f234 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java index 447efea9a..2a404a7cc 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java index b5cdfbc6f..fb7b3d0fd 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java index ae2594c04..c6f11ef01 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java index ac37eec0e..15ba64683 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java index 296f528e6..51b8abcff 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java index ec056de40..56c06003b 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java index bf86248af..0ba1d6114 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java index cb5122bbb..61b70fdc7 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java index 29fc37b0f..f78d13e50 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java index 8394e3f53..45a615773 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java index 9d4bcd866..2afa8bc42 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java index 3561cf53d..3c1251317 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java index c5f1bc764..f60783124 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java index bb4421f24..4e8e34832 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java index 4628703a9..3882324b4 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java index 6581072f0..1721c8236 100644 --- a/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java +++ b/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/ObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java index 338a8cc3d..9804f1a43 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java index e2985f1ce..90822697f 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java index cba276042..31218f129 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java index dbbc98e22..05dce8080 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java index 235c57d51..84111ebb0 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java index 4d223ee23..93ebd6989 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java index 2b18bb3f9..0f7996e00 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java index 26cdadf3a..62716e161 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java index 64a222f45..4518ea83c 100644 --- a/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java +++ b/java-sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java b/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java index 5b83d5e8f..583b2f492 100644 --- a/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java +++ b/json-jq-task/src/main/java/com/netflix/conductor/tasks/json/JsonJqTransform.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java b/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java index be83116bd..73f9fd9a2 100644 --- a/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java +++ b/json-jq-task/src/test/java/com/netflix/conductor/tasks/json/JsonJqTransformTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java index 85b800efb..013f83ed9 100644 --- a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java index 6340c2e54..0a7377d8e 100644 --- a/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java +++ b/kafka/src/main/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java index 2b64c3f1a..f9926e459 100644 --- a/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java +++ b/kafka/src/main/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java index b5d905099..eea69fdc3 100644 --- a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaProducerManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java index 3b9bb1a06..b1360c4e3 100644 --- a/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java +++ b/kafka/src/test/java/com/netflix/conductor/contribs/tasks/kafka/KafkaPublishTaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java index 0534d7436..1be8e89c1 100644 --- a/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java +++ b/kafka/src/test/java/com/netflix/conductor/core/execution/mapper/KafkaPublishTaskMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/licenseheader.txt b/licenseheader.txt index 673d33532..03879c60c 100644 --- a/licenseheader.txt +++ b/licenseheader.txt @@ -1,5 +1,5 @@ /* - * Copyright $YEAR Conductor authors. + * Copyright $YEAR Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java index e0f8f4465..581028981 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java index 287fd5d96..9b52b24e8 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/MetricsRegistryConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java index a2e43333e..38c5756c7 100644 --- a/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java +++ b/metrics/src/main/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java index 4fc03a469..20e84f6fc 100644 --- a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/LoggingMetricsConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java index 0e336ea80..f094d377b 100644 --- a/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java +++ b/metrics/src/test/java/com/netflix/conductor/contribs/metrics/PrometheusMetricsConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java index 42e21f705..22c407892 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java index 5ea81ff67..d6fd7e5e0 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/config/MySQLProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java index d4f6f6ed4..d12dfc52d 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java index 45c13f397..04939469d 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java index ef8bf2b09..badfa51c4 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java index b27beb917..d33f3134b 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/dao/MySQLQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java index ca28f0c50..91a4138aa 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ExecuteFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java index 84b5e28cc..5f39db411 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/LazyToString.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java index 24c9bbb2c..5dd0a8731 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java index 2d462ed61..a76c73027 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/QueryFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java index 5ec2b9011..65c0e4860 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/ResultSetHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java index 098fad478..5aa430f87 100644 --- a/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java +++ b/mysql-persistence/src/main/java/com/netflix/conductor/mysql/util/TransactionalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java index 8975f8641..05790c805 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java index a31a29ce1..d683f8b04 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java index a6872b752..931185a57 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/mysql/dao/MySQLQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java index 0e9ddc0aa..680623f43 100644 --- a/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java +++ b/mysql-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/mysql/MySQLGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java index 473b1b64e..907a072a6 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSAbstractQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java index 25864ee20..de941c15c 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java index 6c17c1c8f..c150e27d5 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/NATSStreamObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java index 2614266ef..3b9e25bd2 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java index 220fd098f..48f29df34 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java index 95d72df4d..325293d1f 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java index 787941a53..6d131dc20 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java index 8c9c2d5af..b864522e7 100644 --- a/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java +++ b/nats-streaming/src/main/java/com/netflix/conductor/contribs/queue/stan/config/NATSStreamProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java index fef121898..7b54b775b 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JetStreamObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java index 5dab5dd65..ddcd7dfa9 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/JsmMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java index 6c498576a..f838afd2c 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSAbstractQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java index 854ae5a05..8c63640e6 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NATSObservableQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java index 9584d5b7e..4d21971ad 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/NatsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java index f9b24cb16..a0fb07b65 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java index a31b98326..36fb7bf4f 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java index 0ee381914..7d8305d06 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/JetStreamProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java index a9f735ac5..7a707f89b 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java index bb96ac01b..d06f318b6 100644 --- a/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java +++ b/nats/src/main/java/com/netflix/conductor/contribs/queue/nats/config/NATSEventQueueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java index 967d77dac..57ea3d908 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java index 1150b31a0..04b348591 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/config/PostgresPayloadProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java index 50feebfc3..d8e1dae16 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java index 67779bd44..456199318 100644 --- a/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java +++ b/postgres-external-storage/src/main/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java index 86f6a5871..1c05b5e4d 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/controller/ExternalPostgresPayloadResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java index 9ec2a14c9..d16132e2b 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadStorageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java index 916bd2bb3..ad7e90892 100644 --- a/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java +++ b/postgres-external-storage/src/test/java/com/netflix/conductor/postgres/storage/PostgresPayloadTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java index 85b8dcce4..62ed27eff 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java index 04a9572a1..5c392cb52 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/config/PostgresProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java index 06173b9df..a0351caab 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java index 9728a9df6..cf6afae4e 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java index 43fa778db..cbd36da28 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresIndexDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java index 0ca4e8e9e..81decfe83 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java index 67dfbcdb6..693b48c06 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/dao/PostgresQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java index a9640217a..1deaa0c23 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecuteFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java index f68a51320..84824cbb4 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ExecutorsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java index 651bfd84e..03d35c31e 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/LazyToString.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java index 2139c09d7..141df11b2 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java index beabb3a2d..ae96161b6 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java index 6ce75615c..d32107bcf 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/QueryFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java index 1878f2b11..2e45806c5 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/ResultSetHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java index a6e5a2ca6..45b94a051 100644 --- a/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java +++ b/postgres-persistence/src/main/java/com/netflix/conductor/postgres/util/TransactionalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java index f8e90bc09..8ab410b3d 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java index a2a891290..c6238b6a8 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresIndexDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java index 4c15590b5..252b096f6 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java index 4bb282782..9cff8e672 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/dao/PostgresQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java index d68aaa626..aac58ef7a 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/performance/PerformanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java index edc235514..2036e2f4a 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/postgres/util/PostgresIndexQueryBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java index 19b5d2074..657b402a4 100644 --- a/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java +++ b/postgres-persistence/src/test/java/com/netflix/conductor/test/integration/grpc/postgres/PostgresGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java index d8f09c379..7107227ab 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java index b43bdc5fe..5abf76300 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java index c51e95d11..be7ab3466 100644 --- a/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java +++ b/redis-concurrency-limit/src/main/java/com/netflix/conductor/redis/limit/config/RedisConcurrentExecutionLimitProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy b/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy index 7abeb2c10..e768a4a3d 100644 --- a/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy +++ b/redis-concurrency-limit/src/test/groovy/com/netflix/conductor/redis/limit/RedisConcurrentExecutionLimitDAOSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java index fb812ccb3..9df1135e2 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java index b979c5025..f5560012f 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/config/RedisLockProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java b/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java index ef833866d..433bacf6f 100644 --- a/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java +++ b/redis-lock/src/main/java/com/netflix/conductor/redislock/lock/RedisLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java index 4e6e06237..184e5ff37 100644 --- a/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java +++ b/redis-lock/src/test/java/com/netflix/conductor/redis/lock/RedisLockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java index 9382acae6..cfeecb7f8 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/AnyRedisCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java index 43c8bb6f4..5fbe20bdb 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/DynomiteClusterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java index 4eb5906fb..e7d2b2fa2 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/InMemoryRedisConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java index a8831e039..03618aad8 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/JedisCommandsConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java index dd1749324..9aa823e1c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisClusterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java index 9aad3a198..6b40e34fd 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisCommonConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java index c06612ece..7813e5519 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java index 1b3762d3d..54abd17ca 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisSentinelConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java index 6ddc53d6c..2156b50fe 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/config/RedisStandaloneConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java index f36af6d06..95a363f87 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/BaseDynoDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java index 1fffaaaa6..76bd72bde 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/DynoQueueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java index c8d6aac9e..2942c18d2 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java index 0ad4f952f..b5fbff6a0 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisExecutionDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java index df715582e..03bc41c0c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisMetadataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java index 2c04307da..053d6a08c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisPollDataDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java index 58779153f..af42082b8 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dao/RedisRateLimitingDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java index ff2094876..562a91ee1 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/ConfigurationHostSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java index eb37a609e..108c1c95c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/LocalhostHostSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java index 96cd6642a..5a76cb901 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/dynoqueue/RedisQueuesShardingStrategyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java index aec6f1357..bf5ea5c97 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisCluster.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java index df57b925c..b6de5b5c1 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisMock.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java index 0b3f7c2bb..902809b3c 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java index 9c6b2906a..27aee1f06 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisSentinel.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java index ca5a9e528..2a5cb2c49 100644 --- a/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java +++ b/redis-persistence/src/main/java/com/netflix/conductor/redis/jedis/JedisStandalone.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java index cce15fc72..3f10f3bf6 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/config/utils/RedisQueuesShardingStrategyProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java index c56d57d59..72c643ca2 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/BaseDynoDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java index 7d55599a2..b2cf461b7 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/DynoQueueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java index c4ab990cd..daca09135 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisEventHandlerDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java index 0065ed6e7..6fe5e61a2 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisExecutionDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java index 64c7722c8..ece7c6e37 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisMetadataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java index 8027529ad..4768946f7 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisPollDataDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java index 28b6e4762..78f8c8bcc 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/dao/RedisRateLimitDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java index 5e13177df..1be01322d 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/ConfigurationHostSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java index 94df53d6d..965313593 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisClusterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java index 6f5099abc..7137c5688 100644 --- a/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java +++ b/redis-persistence/src/test/java/com/netflix/conductor/redis/jedis/JedisSentinelTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java b/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java index 1751d3e4f..3bae355e3 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java +++ b/rest/src/main/java/com/netflix/conductor/rest/config/RequestMappingConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java b/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java index f54f178f9..144ef9349 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java +++ b/rest/src/main/java/com/netflix/conductor/rest/config/RestConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java index ecd36c5d1..bb1c6a6bb 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/AdminResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java index 18c249e26..6aa1aa904 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ApplicationExceptionMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java index 43c5b480c..0339ce2f2 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/EventResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java index ddd602a5e..43b0658a2 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/HealthCheckResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java index 0fa1defc0..5ffda1ed4 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/MetadataResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java index 869998d2c..6651352e9 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/QueueAdminResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java index e7ab74e9b..b606c622d 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/TaskResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java index e70bffc07..68cc36d5d 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/ValidationExceptionMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java index 3077824a6..6c5d99ff1 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java index 713e46040..7200ab756 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java +++ b/rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java b/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java index 9246d9852..017294aba 100644 --- a/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java +++ b/rest/src/main/java/com/netflix/conductor/rest/startup/KitchenSinkInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java index 79137832e..73e7defd4 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/AdminResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java index 6a42354dd..2f6f2c49b 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/EventResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java index 8189e6b56..fc50b15be 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/MetadataResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java index 7ed7f6bfb..5e0e04e13 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/TaskResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java b/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java index f52a9fe17..8c10bc346 100644 --- a/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java +++ b/rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/server/src/main/java/com/netflix/conductor/Conductor.java b/server/src/main/java/com/netflix/conductor/Conductor.java index bf89417c9..10a0f85bd 100644 --- a/server/src/main/java/com/netflix/conductor/Conductor.java +++ b/server/src/main/java/com/netflix/conductor/Conductor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java b/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java index b7996c109..078514d24 100644 --- a/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java +++ b/server/src/test/java/com/netflix/conductor/common/config/ConductorObjectMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy index d40f8619f..0ad2a7f87 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractResiliencySpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy index 3f4cfbdde..974f30001 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/base/AbstractSpecification.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy index 28a0ecf8d..158059bf6 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DecisionTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy index 98e8596e3..5b3327088 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DoWhileSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy index 8ce0e1457..723748fe4 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/DynamicForkJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy index 860940570..60d6d528a 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/EventTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy index d3d98a2ce..faa805186 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExclusiveJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy index da9810330..2edbc6099 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ExternalPayloadStorageSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy index 8ad31e163..26f1955e5 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/FailureWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy index 2fb7a4932..83407e7e8 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/ForkJoinSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy index f6b96e51c..03d077ea0 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRerunSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy index b60b9fddf..279a48fb2 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRestartSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy index ed1c4a54c..56d78bfc2 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/HierarchicalForkJoinSubworkflowRetrySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy index 66f70d048..c05b5c48e 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/JsonJQTransformSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy index 46dbce0a6..fd6c6cb14 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/LambdaAndTerminateTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy index cdd5ef493..548418531 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/NestedForkJoinSubWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy index cdd1e8091..633cca258 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SetVariableTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy index ba59c3529..1134078ac 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SimpleWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy index 2e4fe6250..1af9f2f85 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/StartWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy index 4a1bfe128..350a896f2 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRerunSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy index 43adfeeb3..519d01a18 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRestartSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy index bd740a1e2..4cab35a5a 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowRetrySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy index 7f5a79671..8bc6bbff9 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SubWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy index 39666558e..93c1e6c01 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SwitchTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy index 086452e25..28bfca59b 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/SystemTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy index 1ea47e6ce..f1bcd4dec 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TaskLimitsWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy index 7cb9a9ce4..374fd4c95 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/TestWorkflowSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy index fc6f4069b..d00577ccd 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WaitTaskSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy index 81e752713..3561665be 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/integration/WorkflowAndTaskConfigurationSpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy index 46337739f..0149352bd 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/QueueResiliencySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy index 9b4fe2a3d..4695d6587 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/resiliency/TaskResiliencySpec.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy index 3f9e911ff..52228eb47 100644 --- a/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy +++ b/test-harness/src/test/groovy/com/netflix/conductor/test/util/WorkflowTestUtil.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java index 2688b560e..1979fc543 100644 --- a/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java +++ b/test-harness/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 0b96b2dc8..c110e6730 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index 88110d39c..96ea7783f 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java index f3f4b6271..fb10a2611 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/grpc/GrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java index c05873da9..b57066c01 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/AbstractHttpEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Conductor authors. + * Copyright 2020 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java index 9e49a5b8d..e807f37cf 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/integration/http/HttpEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java b/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java index 86c7be171..18efe513e 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/utils/MockExternalPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Conductor authors. + * Copyright 2021 Conductor 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 diff --git a/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java b/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java index a12be85ab..ae1fc9ac9 100644 --- a/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java +++ b/test-harness/src/test/java/com/netflix/conductor/test/utils/UserTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Conductor authors. + * Copyright 2022 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java index 892cdbdae..b7523573d 100644 --- a/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java +++ b/test-util/src/test/java/com/netflix/conductor/ConductorTestApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java index 90fe5379f..76e6e6dab 100644 --- a/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java +++ b/test-util/src/test/java/com/netflix/conductor/common/config/TestObjectMapperConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java index 5d4f248b6..4065241a5 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/AbstractEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java index d21148311..5f53592bb 100644 --- a/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java +++ b/test-util/src/test/java/com/netflix/conductor/test/integration/grpc/AbstractGrpcEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java index 791a6c5f2..b80ebf98f 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWithTTLWorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java index 017fb69e3..d468ba960 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java index 18e0b4ba1..7e2fad5cb 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowListenerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java index 0745e2604..26dfdf364 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/archive/ArchivingWorkflowStatusListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java index f58578fed..6c0812061 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java index dbb941f88..e0c767978 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java index f714e2387..d7e50a5a3 100644 --- a/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java +++ b/workflow-event-listener/src/main/java/com/netflix/conductor/contribs/listener/conductorqueue/ConductorQueueStatusPublisherProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java index 551daaec8..a3839bca5 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/contribs/listener/ArchivingWorkflowStatusListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 diff --git a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java index be5ec0aa1..3aa8b072b 100644 --- a/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java +++ b/workflow-event-listener/src/test/java/com/netflix/conductor/test/listener/WorkflowStatusPublisherIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Conductor authors. + * Copyright 2023 Conductor 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 From b1c5d6892904836ea5982f5cc0fd4deb84e1f730 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:42:41 -0800 Subject: [PATCH 057/202] fixes --- es6-persistence/build.gradle | 4 ++-- .../netflix/conductor/es6/config/ElasticSearchConditions.java | 2 +- .../netflix/conductor/es6/config/ElasticSearchProperties.java | 2 +- .../conductor/es6/config/ElasticSearchV6Configuration.java | 2 +- .../java/com/netflix/conductor/es6/config/IsHttpProtocol.java | 2 +- .../java/com/netflix/conductor/es6/config/IsTcpProtocol.java | 2 +- .../conductor/es6/dao/index/BulkRequestBuilderWrapper.java | 2 +- .../netflix/conductor/es6/dao/index/BulkRequestWrapper.java | 2 +- .../netflix/conductor/es6/dao/index/ElasticSearchBaseDAO.java | 2 +- .../netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java | 2 +- .../conductor/es6/dao/index/ElasticSearchRestDAOV6.java | 2 +- .../netflix/conductor/es6/dao/query/parser/Expression.java | 2 +- .../conductor/es6/dao/query/parser/FilterProvider.java | 2 +- .../conductor/es6/dao/query/parser/GroupedExpression.java | 2 +- .../com/netflix/conductor/es6/dao/query/parser/NameValue.java | 2 +- .../conductor/es6/dao/query/parser/internal/AbstractNode.java | 2 +- .../conductor/es6/dao/query/parser/internal/BooleanOp.java | 2 +- .../conductor/es6/dao/query/parser/internal/ComparisonOp.java | 2 +- .../conductor/es6/dao/query/parser/internal/ConstValue.java | 2 +- .../dao/query/parser/internal/FunctionThrowingException.java | 2 +- .../conductor/es6/dao/query/parser/internal/ListConst.java | 2 +- .../netflix/conductor/es6/dao/query/parser/internal/Name.java | 2 +- .../es6/dao/query/parser/internal/ParserException.java | 2 +- .../conductor/es6/dao/query/parser/internal/Range.java | 2 +- .../conductor/es6/dao/index/ElasticSearchDaoBaseTest.java | 2 +- .../conductor/es6/dao/index/ElasticSearchRestDaoBaseTest.java | 2 +- .../netflix/conductor/es6/dao/index/ElasticSearchTest.java | 2 +- .../conductor/es6/dao/index/TestElasticSearchDAOV6.java | 2 +- .../conductor/es6/dao/index/TestElasticSearchDAOV6Batch.java | 2 +- .../conductor/es6/dao/index/TestElasticSearchRestDAOV6.java | 2 +- .../es6/dao/index/TestElasticSearchRestDAOV6Batch.java | 2 +- .../conductor/es6/dao/query/parser/TestExpression.java | 2 +- .../es6/dao/query/parser/internal/TestAbstractParser.java | 2 +- .../es6/dao/query/parser/internal/TestBooleanOp.java | 2 +- .../es6/dao/query/parser/internal/TestComparisonOp.java | 2 +- .../es6/dao/query/parser/internal/TestConstValue.java | 2 +- .../conductor/es6/dao/query/parser/internal/TestName.java | 2 +- .../test/java/com/netflix/conductor/es6/utils/TestUtils.java | 2 +- 38 files changed, 39 insertions(+), 39 deletions(-) diff --git a/es6-persistence/build.gradle b/es6-persistence/build.gradle index 9465340f6..5ebed8636 100644 --- a/es6-persistence/build.gradle +++ b/es6-persistence/build.gradle @@ -37,6 +37,6 @@ dependencies { switch (org.gradle.internal.os.OperatingSystem.current()) { case org.gradle.internal.os.OperatingSystem.MAC_OS: - tasks.forEach(task -> task.onlyIf { project.hasProperty('ES6Test') }) + //tasks.forEach(task -> task.onlyIf { project.hasProperty('ES6Test') }) break; -} \ No newline at end of file +} diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchConditions.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchConditions.java index e8edae218..069a0e8dc 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchConditions.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchConditions.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchProperties.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchProperties.java index 50500af64..106b5413c 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchProperties.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchV6Configuration.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchV6Configuration.java index a16bea44d..3282c5c26 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchV6Configuration.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/ElasticSearchV6Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsHttpProtocol.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsHttpProtocol.java index 2437e1a22..fea331c64 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsHttpProtocol.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsHttpProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsTcpProtocol.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsTcpProtocol.java index accf3c468..a0bdadd4b 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsTcpProtocol.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/config/IsTcpProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestBuilderWrapper.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestBuilderWrapper.java index d0056c729..9f9a77362 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestBuilderWrapper.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestBuilderWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestWrapper.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestWrapper.java index d33aedf5c..aee726dac 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestWrapper.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/BulkRequestWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchBaseDAO.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchBaseDAO.java index 38733977e..268c3a002 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchBaseDAO.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchBaseDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java index bbf05423e..a64ed215d 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchDAOV6.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java index e51d13341..3fc47d811 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDAOV6.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/Expression.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/Expression.java index 9ab2dfe41..5466a2845 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/Expression.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/Expression.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/FilterProvider.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/FilterProvider.java index 3c145975d..fca92d8b5 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/FilterProvider.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/FilterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/GroupedExpression.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/GroupedExpression.java index 90fc3b489..168b6b982 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/GroupedExpression.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/GroupedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/NameValue.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/NameValue.java index 68c1e5af2..95e04e2a4 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/NameValue.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/NameValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/AbstractNode.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/AbstractNode.java index 2d5fe84ad..20d4a5b26 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/AbstractNode.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/AbstractNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/BooleanOp.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/BooleanOp.java index ccdc15a55..6f435cd5a 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/BooleanOp.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/BooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ComparisonOp.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ComparisonOp.java index 10d44863d..66450ead0 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ComparisonOp.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ConstValue.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ConstValue.java index 47bebce6b..080330cb7 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ConstValue.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/FunctionThrowingException.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/FunctionThrowingException.java index afb9b7df1..8d1b055a7 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/FunctionThrowingException.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/FunctionThrowingException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ListConst.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ListConst.java index 3efda6b3e..827a302f3 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ListConst.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ListConst.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Name.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Name.java index a26945602..3bf6bf8c1 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Name.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Name.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ParserException.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ParserException.java index 0b946c475..f0a23913c 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ParserException.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/ParserException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Range.java b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Range.java index aa4c66f7a..1677df737 100644 --- a/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Range.java +++ b/es6-persistence/src/main/java/com/netflix/conductor/es6/dao/query/parser/internal/Range.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchDaoBaseTest.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchDaoBaseTest.java index 529f22e12..337bb3135 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchDaoBaseTest.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchDaoBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDaoBaseTest.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDaoBaseTest.java index 59584e1dc..701926be7 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDaoBaseTest.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchRestDaoBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java index 2cbc2ff86..269157925 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/ElasticSearchTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Netflix, Inc. + * Copyright 2021 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6.java index afb54e8cb..d56326b39 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6Batch.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6Batch.java index 8a1f14ef9..b8f33be55 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6Batch.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchDAOV6Batch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6.java index 8a2d6d52c..c8de43025 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6Batch.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6Batch.java index 98f503923..c2b053739 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6Batch.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/index/TestElasticSearchRestDAOV6Batch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Netflix, Inc. + * Copyright 2022 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/TestExpression.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/TestExpression.java index 8ebe4c83f..212773e8f 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/TestExpression.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/TestExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestAbstractParser.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestAbstractParser.java index 36a2adc48..fe8792b2f 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestAbstractParser.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestAbstractParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestBooleanOp.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestBooleanOp.java index 216c289a2..308063e9a 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestBooleanOp.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestBooleanOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestComparisonOp.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestComparisonOp.java index 3878947df..d9a90b18f 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestComparisonOp.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestComparisonOp.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestConstValue.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestConstValue.java index 2ae311d54..7d4f04690 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestConstValue.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestConstValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestName.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestName.java index 3de5abdc0..f5a366360 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestName.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/dao/query/parser/internal/TestName.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 diff --git a/es6-persistence/src/test/java/com/netflix/conductor/es6/utils/TestUtils.java b/es6-persistence/src/test/java/com/netflix/conductor/es6/utils/TestUtils.java index 4cb8d234d..9f1e822a3 100644 --- a/es6-persistence/src/test/java/com/netflix/conductor/es6/utils/TestUtils.java +++ b/es6-persistence/src/test/java/com/netflix/conductor/es6/utils/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Netflix, Inc. + * Copyright 2020 Conductor 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 From 9d2af68ebb1bbc924bff3c0121d2b4ccebdc33fe Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:52:28 -0800 Subject: [PATCH 058/202] Update contributing.md --- docs/docs/resources/contributing.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/docs/resources/contributing.md b/docs/docs/resources/contributing.md index 4588a55a9..c92937904 100644 --- a/docs/docs/resources/contributing.md +++ b/docs/docs/resources/contributing.md @@ -5,25 +5,27 @@ This guide helps to find the most efficient way to contribute, ask questions, an Code of conduct ----- -Please review our [code of conduct](code-of-conduct.md). +Please review our [Code of Conduct](https://orkes.io/orkes-conductor-community-code-of-conduct) I have a question! ----- -We have a dedicated [discussion forum](https://github.com/Netflix/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. +We have a dedicated [discussion forum](https://github.com/conductor-oss/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. *Please do not create issues to ask questions.* +Conductor users hangout in the [Slack channel](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg). Join the channel for more real-time communication! + I want to contribute! ------ We welcome Pull Requests and already had many outstanding community contributions! Creating and reviewing Pull Requests take considerable time. This section helps you to set up a smooth Pull Request experience. -The stable branch is [main](https://github.com/Netflix/conductor/tree/main). +The stable branch is [main](https://github.com/conductor-oss/conductor/tree/main). -Please create pull requests for your contributions against [main](https://github.com/Netflix/conductor/tree/main) only. +Please create pull requests for your contributions against [main](https://github.com/conductor-oss/conductor/tree/main) only. -It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/Netflix/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. +It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/conductor-oss/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. Also, consider that not every feature is a good fit for Conductor. A few things to consider are: @@ -32,6 +34,7 @@ Also, consider that not every feature is a good fit for Conductor. A few things * Does it require new dependencies (this is rarely acceptable for core modules) * Should the feature be opt-in or enabled by default. For integration with a new Queuing recipe or persistence module, a separate module which can be optionally enabled is the right choice. * Should the feature be implemented in the main Conductor repository, or would it be better to set up a separate repository? Especially for integration with other systems, a separate repository is often the right choice because the life-cycle of it will be different. +* Is it part of the Conductor project roadmap? Of course, for more minor bug fixes and improvements, the process can be more light-weight. @@ -44,7 +47,7 @@ If you found a bug, it is much appreciated if you create an issue. Please includ I have a great idea for a new feature ---- -Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/Netflix/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. +Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/conductor-oss/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. Once we have decided on a direction, it's time to summarize the idea by creating a new issue. From c8a7164812b91dee4ac1bbba6def30e4961bf7ec Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:53:12 -0800 Subject: [PATCH 059/202] Update CONTRIBUTING.md --- CONTRIBUTING.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e94e3c16a..0b27abb9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,25 +5,27 @@ This guide helps to find the most efficient way to contribute, ask questions, an Code of conduct ----- -Please review our [code of conduct](code-of-conduct.md). +Please review our [Code of Conduct](https://orkes.io/orkes-conductor-community-code-of-conduct) I have a question! ----- -We have a dedicated [discussion forum](https://github.com/Netflix/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. +We have a dedicated [discussion forum](https://github.com/conductor-oss/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. *Please do not create issues to ask questions.* +Conductor users hangout in the [Slack channel](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg). Join the channel for more real-time communication! + I want to contribute! ------ We welcome Pull Requests and already had many outstanding community contributions! Creating and reviewing Pull Requests take considerable time. This section helps you to set up a smooth Pull Request experience. -The stable branch is [main](https://github.com/Netflix/conductor/tree/main). +The stable branch is [main](https://github.com/conductor-oss/conductor/tree/main). -Please create pull requests for your contributions against [main](https://github.com/Netflix/conductor/tree/main) only. +Please create pull requests for your contributions against [main](https://github.com/conductor-oss/conductor/tree/main) only. -It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/Netflix/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. +It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/conductor-oss/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. Also, consider that not every feature is a good fit for Conductor. A few things to consider are: @@ -32,6 +34,7 @@ Also, consider that not every feature is a good fit for Conductor. A few things * Does it require new dependencies (this is rarely acceptable for core modules) * Should the feature be opt-in or enabled by default. For integration with a new Queuing recipe or persistence module, a separate module which can be optionally enabled is the right choice. * Should the feature be implemented in the main Conductor repository, or would it be better to set up a separate repository? Especially for integration with other systems, a separate repository is often the right choice because the life-cycle of it will be different. +* Is it part of the Conductor project roadmap? Of course, for more minor bug fixes and improvements, the process can be more light-weight. @@ -44,7 +47,7 @@ If you found a bug, it is much appreciated if you create an issue. Please includ I have a great idea for a new feature ---- -Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/Netflix/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. +Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/conductor-oss/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. Once we have decided on a direction, it's time to summarize the idea by creating a new issue. From e90d7079929806c35a4abc7baa745ab0eb91167b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 11:55:24 -0800 Subject: [PATCH 060/202] clean up --- CONTRIBUTING.md | 3 -- docs/docs/resources/contributing.md | 63 ------------------------ docs/docs/resources/license.md | 15 ------ docs/docs/resources/related.md | 74 ----------------------------- 4 files changed, 155 deletions(-) delete mode 100644 docs/docs/resources/contributing.md delete mode 100644 docs/docs/resources/license.md delete mode 100644 docs/docs/resources/related.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b27abb9d..b102a7a37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,9 +55,6 @@ Once we have decided on a direction, it's time to summarize the idea by creating We use [spotless](https://github.com/diffplug/spotless) to enforce consistent code style for the project, so make sure to run `gradlew spotlessApply` to fix any violations after code changes. ## License - -By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/Netflix/conductor/blob/master/LICENSE - All files are released with the Apache 2.0 license. diff --git a/docs/docs/resources/contributing.md b/docs/docs/resources/contributing.md deleted file mode 100644 index c92937904..000000000 --- a/docs/docs/resources/contributing.md +++ /dev/null @@ -1,63 +0,0 @@ -# Contributing -Thanks for your interest in Conductor! -This guide helps to find the most efficient way to contribute, ask questions, and report issues. - -Code of conduct ------ - -Please review our [Code of Conduct](https://orkes.io/orkes-conductor-community-code-of-conduct) - -I have a question! ------ - -We have a dedicated [discussion forum](https://github.com/conductor-oss/conductor/discussions) for asking "how to" questions and to discuss ideas. The discussion forum is a great place to start if you're considering creating a feature request or work on a Pull Request. -*Please do not create issues to ask questions.* - -Conductor users hangout in the [Slack channel](https://join.slack.com/t/orkes-conductor/shared_invite/zt-xyxqyseb-YZ3hwwAgHJH97bsrYRnSZg). Join the channel for more real-time communication! - -I want to contribute! ------- - -We welcome Pull Requests and already had many outstanding community contributions! -Creating and reviewing Pull Requests take considerable time. This section helps you to set up a smooth Pull Request experience. - -The stable branch is [main](https://github.com/conductor-oss/conductor/tree/main). - -Please create pull requests for your contributions against [main](https://github.com/conductor-oss/conductor/tree/main) only. - -It's a great idea to discuss the new feature you're considering on the [discussion forum](https://github.com/conductor-oss/conductor/discussions) before writing any code. There are often different ways you can implement a feature. Getting some discussion about different options helps shape the best solution. When starting directly with a Pull Request, there is the risk of having to make considerable changes. Sometimes that is the best approach, though! Showing an idea with code can be very helpful; be aware that it might be throw-away work. Some of our best Pull Requests came out of multiple competing implementations, which helped shape it to perfection. - -Also, consider that not every feature is a good fit for Conductor. A few things to consider are: - -* Is it increasing complexity for the user, or might it be confusing? -* Does it, in any way, break backward compatibility (this is seldom acceptable) -* Does it require new dependencies (this is rarely acceptable for core modules) -* Should the feature be opt-in or enabled by default. For integration with a new Queuing recipe or persistence module, a separate module which can be optionally enabled is the right choice. -* Should the feature be implemented in the main Conductor repository, or would it be better to set up a separate repository? Especially for integration with other systems, a separate repository is often the right choice because the life-cycle of it will be different. -* Is it part of the Conductor project roadmap? - -Of course, for more minor bug fixes and improvements, the process can be more light-weight. - -We'll try to be responsive to Pull Requests. Do keep in mind that because of the inherently distributed nature of open source projects, responses to a PR might take some time because of time zones, weekends, and other things we may be working on. - -I want to report an issue ------ - -If you found a bug, it is much appreciated if you create an issue. Please include clear instructions on how to reproduce the issue, or even better, include a test case on a branch. Make sure to come up with a descriptive title for the issue because this helps while organizing issues. - -I have a great idea for a new feature ----- -Many features in Conductor have come from ideas from the community. If you think something is missing or certain use cases could be supported better, let us know! You can do so by opening a discussion on the [discussion forum](https://github.com/conductor-oss/conductor/discussions). Provide as much relevant context to why and when the feature would be helpful. Providing context is especially important for "Support XYZ" issues since we might not be familiar with what "XYZ" is and why it's useful. If you have an idea of how to implement the feature, include that as well. - -Once we have decided on a direction, it's time to summarize the idea by creating a new issue. - -## Code Style -We use [spotless](https://github.com/diffplug/spotless) to enforce consistent code style for the project, so make sure to run `gradlew spotlessApply` to fix any violations after code changes. - -## License - -By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/Netflix/conductor/blob/master/LICENSE - -All files are released with the Apache 2.0 license. - - diff --git a/docs/docs/resources/license.md b/docs/docs/resources/license.md deleted file mode 100644 index 518de4064..000000000 --- a/docs/docs/resources/license.md +++ /dev/null @@ -1,15 +0,0 @@ -# License - -Copyright 2022 Netflix, Inc. - -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](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. diff --git a/docs/docs/resources/related.md b/docs/docs/resources/related.md deleted file mode 100644 index 9a293f152..000000000 --- a/docs/docs/resources/related.md +++ /dev/null @@ -1,74 +0,0 @@ -# Community projects related to Conductor - -## Client SDKs - -Further, all of the (non-Java) SDKs have a new GitHub home: the Conductor SDK repository is your new source for Conductor SDKs: - -* [Golang](https://github.com/conductor-sdk/conductor-go) -* [Python](https://github.com/conductor-sdk/conductor-python) -* [C#](https://github.com/conductor-sdk/conductor-csharp) -* [Clojure](https://github.com/conductor-sdk/conductor-clojure) - -All contributions on the above client sdks can be made on [Conductor SDK](https://github.com/conductor-sdk) repository. - -## Microservices operations - -* https://github.com/flaviostutz/schellar - Schellar is a scheduler tool for instantiating Conductor workflows from time to time, mostly like a cron job, but with transport of input/output variables between calls. - -* https://github.com/flaviostutz/backtor - Backtor is a backup scheduler tool that uses Conductor workers to handle backup operations and decide when to expire backups (ex.: keep backup 3 days, 2 weeks, 2 months, 1 semester) - -* https://github.com/cquon/conductor-tools - Conductor CLI for launching workflows, polling tasks, listing running tasks etc - - -## Conductor deployment - -* https://github.com/flaviostutz/conductor-server - Docker container for running Conductor with Prometheus metrics plugin installed and some tweaks to ease provisioning of workflows from json files embedded to the container - -* https://github.com/flaviostutz/conductor-ui - Docker container for running Conductor UI so that you can easily scale UI independently - -* https://github.com/flaviostutz/elasticblast - "Elasticsearch to Bleve" bridge tailored for running Conductor on top of Bleve indexer. The footprint of Elasticsearch may cost too much for small deployments on Cloud environment. - -* https://github.com/mohelsaka/conductor-prometheus-metrics - Conductor plugin for exposing Prometheus metrics over path '/metrics' - -## OAuth2.0 Security Configuration -Forked Repository - [Conductor (Secure)](https://github.com/maheshyaddanapudi/conductor/tree/oauth2) - -[OAuth2.0 Role Based Security!](https://github.com/maheshyaddanapudi/conductor/blob/oauth2/SECURITY.md) - Spring Security with easy configuration to secure the Conductor server APIs. - -Docker image published to [Docker Hub](https://hub.docker.com/repository/docker/conductorboot/server) - -## Conductor Worker utilities - -* https://github.com/ggrcha/conductor-go-client - Conductor Golang client for writing Workers in Golang - -* https://github.com/courosh12/conductor-dotnet-client - Conductor DOTNET client for writing Workers in DOTNET - * https://github.com/TwoUnderscorez/serilog-sinks-conductor-task-log - Serilog sink for sending worker log events to Netflix Conductor - -* https://github.com/davidwadden/conductor-workers - Various ready made Conductor workers for common operations on some platforms (ex.: Jira, Github, Concourse) - -## Conductor Web UI - -* https://github.com/maheshyaddanapudi/conductor-ng-ui - Angular based - Conductor Workflow Management UI - -## Conductor Persistence - -### Mongo Persistence - -* https://github.com/maheshyaddanapudi/conductor/tree/mongo_persistence - With option to use Mongo Database as persistence unit. - * Mongo Persistence / Option to use Mongo Database as persistence unit. - * Docker Compose example with MongoDB Container. - -### Oracle Persistence - -* https://github.com/maheshyaddanapudi/conductor/tree/oracle_persistence - With option to use Oracle Database as persistence unit. - * Oracle Persistence / Option to use Oracle Database as persistence unit : version > 12.2 - Tested well with 19C - * Docker Compose example with Oracle Container. - -## Schedule Conductor Workflow -* https://github.com/jas34/scheduledwf - It solves the following problem statements: - * At times there are use cases in which we need to run some tasks/jobs only at a scheduled time. - * In microservice architecture maintaining schedulers in various microservices is a pain. - * We should have a central dedicate service that can do scheduling for us and provide a trigger to a microservices at expected time. -* It offers an additional module `io.github.jas34.scheduledwf.config.ScheduledWfServerModule` built on the existing core -of conductor and does not require deployment of any additional service. -For more details refer: [Schedule Conductor Workflows](https://jas34.github.io/scheduledwf) and [Capability In Conductor To Schedule Workflows](https://github.com/Netflix/conductor/discussions/2256) \ No newline at end of file From d14d6b221874e39c1e08ef1573f03be05faa663b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Wed, 20 Dec 2023 12:01:54 -0800 Subject: [PATCH 061/202] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 61 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 425e79778..7671bb260 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1 +1,60 @@ -[Code of Conduct](https://orkes.io/orkes-conductor-community-code-of-conduct) \ No newline at end of file +Hello valued community members! 👋 + +Our Conductor community has grown tremendously, and as part of ensuring a harmonious experience for everyone, +we've outlined some guidelines that we'd love for all members to uphold. +Our community thrives when everyone engages with kindness, respect, and a collaborative spirit. Here's what we hope to see: + + +### 1. Maintain a Positive Tone. +Every interaction is an opportunity to lift someone up. Let's ensure our words and actions reflect optimism and encouragement. + +### 2.Be Respectful to the Community +Every member here comes with a unique background and perspective. Please honor those differences by being courteous, considerate, and open-minded. +Remember, mutual respect is the foundation of a thriving community. Be careful in the words that you choose. +We are a community of professionals, and we conduct ourselves professionally. +Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behaviors aren't acceptable. +This includes, but is not limited to: +* Violent threats or language directed against another person Discriminatory jokes and language +* Posting sexualized language or imagery +* Posting (or threatening to post) other people's personally identifying information (“doxing”) +* Personal insults, especially those using racist or sexist terms +* Unwelcome sexual attention +* Advocating for, or encouraging, any of the above behavior +* Repeated harassment of others. In general, if someone asks you to stop, then stop + +### 3. Preserve Our Community's Unity +We understand that as we grow, there might be differing opinions and interests. +However, we kindly request not to create splinter groups or fork out the community. +Let's work through our differences and continue building this space together. + +### 4. Focus on Constructive Discussions +We all have moments of frustration, but let's express ourselves in ways that are constructive. +Avoid comments that could come off as sarcastic, condescending, or disdainful. +Remember, it's always possible to give feedback or express disagreement without belittling others. +We are here to learn from each other and make Conductor the best platform out there. +A big part of that are the exchanges of ideas and approaches that are grounded in data and sound reasoning. +We kindly request that you adhere to that pattern and be thoughtful and responsible in your discussions. +This also means that you are required to have discussions focused on the community and not on promotion of any services, products or goods. + +### 5. When we disagree, try to understand why. +Disagreements, both social and technical, happen all the time and this community is no exception. +It is important that we resolve disagreements and differing views constructively. +Remember that we’re all different. The strength of this community comes from its varied community of people from a wide range of backgrounds. +Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. +Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. +Instead, focus on helping to resolve issues and learning from mistakes. +Our community's strength lies in our collective spirit. +By following these guidelines, we ensure that our community remains an inspiring, respectful, and welcoming place for everyone. +If you have any concerns or suggestions or if you need to report on any behavior that violates this Code of Conduct, please feel free to reach out to the admins - community@orkes.io. +Let's continue to support and uplift each other! + +### Enforcement +We have a variety of ways of enforcing the code of conduct, including, but not limited to +* Asking you nicely to knock it off +* Asking you less nicely +* Temporary or permanent suspension of the account +* Removal of privileges and/or adding restrictions to the account +* Removal of content +* Banning from the community + +Thank you, \ No newline at end of file From 42fd59e1bae2c96e008c2801f93588772740222c Mon Sep 17 00:00:00 2001 From: Vasiliy Pankov Date: Thu, 21 Dec 2023 00:27:41 +0400 Subject: [PATCH 062/202] Community docs initial (#22) Changes to make github pages based docs work --- .github/generate_gh_pages.yml | 20 + docs/assets/images/favicon.png | Bin 0 -> 20196 bytes docs/css/custom.css | 205 ++++++++ .../architecture/PollTimeoutSeconds.png | Bin 0 -> 76891 bytes .../architecture}/ResponseTimeoutSeconds.png | Bin .../architecture}/TaskFailure.png | Bin .../architecture}/TimeoutSeconds.png | Bin .../architecture}/conductor-architecture.png | Bin .../architecture}/dag_workflow.png | Bin .../architecture}/dag_workflow2.png | Bin .../architecture}/directed-acyclic-graph.md | 10 +- .../architecture}/directed_graph.png | Bin .../architecture/index.md} | 8 +- .../architecture}/overview.png | Bin .../architecture}/pirate_graph.gif | Bin .../architecture}/regular_graph.png | Bin .../architecture}/task_states.png | Bin .../architecture/tasklifecycle.md | 38 +- .../architecture}/technicaldetails.md | 4 +- docs/{docs => devguide}/bestpractices.md | 2 + docs/devguide/concepts/index.md | 17 + docs/devguide/concepts/tasks.md | 32 ++ .../intro.md => devguide/concepts/why.md} | 8 +- docs/devguide/concepts/workers.md | 11 + docs/devguide/concepts/workflows.md | 13 + docs/{docs => devguide}/faq.md | 38 +- .../how-tos/Monitoring/Conductor-LogLevel.md | 0 .../how-tos/Tasks/creating-tasks.md | 13 +- .../how-tos/Tasks/dynamic-vs-switch-tasks.md | 18 + .../how-tos/Tasks/extending-system-tasks.md | 4 +- .../how-tos/Tasks/monitoring-task-queues.md | 17 +- .../how-tos/Tasks/reusing-tasks.md | 4 - .../how-tos/Tasks/task-configurations.md | 14 +- .../how-tos/Tasks/task-inputs.md | 10 +- .../how-tos/Tasks/task-timeouts.md | 10 +- .../how-tos/Tasks/updating-tasks.md | 14 +- .../Workers/build-a-golang-task-worker.md | 8 +- .../Workers/build-a-java-task-worker.md | 6 +- .../Workers/build-a-python-task-worker.md | 8 +- .../how-tos/Workflows/debugging-workflows.md | 10 +- .../how-tos/Workflows/handling-errors.md | 4 +- .../how-tos/Workflows/searching-workflows.md | 12 - .../how-tos/Workflows/starting-workflows.md | 6 +- .../how-tos/Workflows/updating-workflows.md | 6 +- .../how-tos/Workflows/versioning-workflows.md | 4 - .../Workflows/view-workflow-executions.md | 18 +- .../how-tos/Workflows}/workflow_debugging.png | Bin .../Workflows}/workflow_execution_view.png | Bin .../how-tos/Workflows}/workflow_task_fail.png | Bin docs/{docs => devguide}/labs/eventhandlers.md | 25 +- .../labs/first-workflow.md} | 35 +- .../labs/img/EventHandlerCycle.png | Bin .../labs/img/bgnr_complete_workflow.png | Bin .../labs/img/bgnr_state_scheduled.png | Bin .../labs/img/bgnr_systask_state.png | Bin docs/devguide/labs/index.md | 23 + docs/{docs => devguide}/labs/kitchensink.md | 13 +- .../img => devguide/labs}/kitchensink.png | Bin .../labs}/metadataWorkflowPost.png | Bin .../labs}/metadataWorkflowRun.png | Bin .../labs}/uiWorkflowDefinition.png | Bin .../labs}/uiWorkflowDefinitionVisual.png | Bin .../labs}/workflowLoaded.png | Bin .../labs}/workflowRunIdCopy.png | Bin .../running}/conductorUI.png | Bin .../running}/docker.md | 81 ++-- .../running}/hosted.md | 0 .../running}/source.md | 10 +- .../tutorial => devguide/running}/swagger.png | Bin docs/docs/apispec.md | 173 ------- docs/docs/configuration/sysoperator.md | 20 - docs/docs/configuration/systask.md | 256 ---------- docs/docs/configuration/taskdef.md | 112 ----- docs/docs/configuration/workerdef.md | 14 - docs/docs/configuration/workflowdef.md | 229 --------- docs/docs/css/custom.css | 313 ------------- docs/docs/gettingstarted/basicconcepts.md | 37 -- docs/docs/gettingstarted/client.md | 21 - docs/docs/gettingstarted/steps.md | 36 -- docs/docs/googleba55068fa3e0e553.html | 1 - .../how-tos/Tasks/dynamic-vs-switch-tasks.md | 22 - docs/docs/how-tos/Test/testing-workflows.md | 34 -- docs/docs/img/logo.png | Bin 22312 -> 0 bytes docs/docs/img/logo_dark_background.png | Bin 21552 -> 0 bytes docs/docs/img/netflix.png | Bin 16470 -> 0 bytes docs/docs/img/task_states.svg | 4 - docs/docs/img/tutorial/SubWorkflow.png | Bin 8035 -> 0 bytes docs/docs/img/tutorial/Sub_Workflow_Run.png | Bin 87111 -> 0 bytes docs/docs/img/tutorial/Switch_Default.png | Bin 86478 -> 0 bytes docs/docs/img/tutorial/Switch_Fedex.png | Bin 44581 -> 0 bytes docs/docs/img/tutorial/Switch_Workflow.png | Bin 21721 -> 0 bytes docs/docs/img/tutorial/Terminate_Task.png | Bin 22374 -> 0 bytes docs/docs/img/tutorial/Terminate_Task_Run.png | Bin 96531 -> 0 bytes .../tutorial/Terminate_Task_Successful.png | Bin 90674 -> 0 bytes docs/docs/img/tutorial/conductorConsole.png | Bin 35278 -> 0 bytes docs/docs/img/tutorial/conductorHome.png | Bin 125372 -> 0 bytes docs/docs/img/tutorial/conductorUIHome.png | Bin 218119 -> 0 bytes docs/docs/img/tutorial/dockerHome.png | Bin 654002 -> 0 bytes .../docs/img/tutorial/docsVersionDropdown.png | Bin 25102 -> 0 bytes docs/docs/img/tutorial/elasticSearchHome.png | Bin 99517 -> 0 bytes .../docs/img/tutorial/firstWorkerWorkflow.png | Bin 6852 -> 0 bytes docs/docs/img/tutorial/localeDropdown.png | Bin 30020 -> 0 bytes .../tutorial/successfulWorkerExecution.png | Bin 310030 -> 0 bytes docs/docs/labs/beginner.md | 439 ------------------ .../reference-docs/archival-of-workflows.md | 13 - docs/docs/reference-docs/event-task.md | 95 ---- docs/docs/reference-docs/fork-task.md | 140 ------ docs/docs/reference-docs/http-task.md | 144 ------ docs/docs/reference-docs/human-task.md | 25 - docs/docs/reference-docs/inline-task.md | 76 --- docs/docs/reference-docs/wait-task.md | 74 --- .../advanced}/annotation-processor.md | 7 - .../advanced/archival-of-workflows.md | 13 + .../advanced}/azureblob-storage.md | 0 .../advanced}/extend.md | 6 + .../advanced}/externalpayloadstorage.md | 6 +- .../advanced}/isolationgroups.md | 0 .../advanced}/redis.md | 0 docs/documentation/api/index.md | 13 + docs/documentation/api/metadata.md | 18 + .../api}/startworkflow.md | 46 +- docs/documentation/api/task.md | 37 ++ .../api}/taskdomains.md | 50 +- docs/documentation/api/workflow.md | 86 ++++ .../clientsdks}/clojure-sdk.md | 16 +- .../clientsdks}/csharp-sdk.md | 2 +- .../clientsdks}/go-sdk.md | 2 +- docs/documentation/clientsdks/index.md | 10 + .../clientsdks}/java-sdk.md | 23 +- .../clientsdks}/python-sdk.md | 12 +- .../configuration/eventhandlers.md | 43 +- docs/documentation/configuration/taskdef.md | 117 +++++ .../configuration/workflowdef/index.md | 302 ++++++++++++ .../operators}/ShippingWorkflow.png | Bin .../operators}/ShippingWorkflowRunning.png | Bin .../operators}/ShippingWorkflowUPS.png | Bin .../workflowdef/operators}/Switch_Fedex.png | Bin .../workflowdef/operators}/Terminate_Task.png | Bin .../workflowdef/operators}/do-while-task.md | 32 +- .../operators}/dynamic-fork-task.md | 117 ++--- .../operators}/dynamic-task-diagram.png | Bin .../workflowdef/operators}/dynamic-task.md | 49 +- .../operators}/fork-task-diagram.png | Bin .../workflowdef/operators/fork-task.md | 123 +++++ .../workflowdef/operators/index.md | 19 + .../workflowdef/operators}/join-task.md | 49 +- .../operators}/set-variable-task.md | 29 +- .../operators}/start-workflow-task.md | 32 +- .../operators}/sub-workflow-task.md | 84 ++-- .../operators}/subworkflow_diagram.png | Bin .../workflowdef/operators}/switch-task.md | 50 +- .../workflowdef/operators}/terminate-task.md | 57 +-- .../workflowdef/operators}/workflow_fork.png | Bin .../workflowdef/systemtasks/event-task.md | 83 ++++ .../workflowdef/systemtasks/http-task.md | 141 ++++++ .../workflowdef/systemtasks/human-task.md | 48 ++ .../workflowdef/systemtasks/index.md | 13 + .../workflowdef/systemtasks/inline-task.md | 67 +++ .../systemtasks}/json-jq-transform-task.md | 85 ++-- .../systemtasks}/kafka-publish-task.md | 47 +- .../workflowdef/systemtasks/wait-task.md | 52 +++ .../{docs => documentation}/metrics/client.md | 0 .../{docs => documentation}/metrics/server.md | 0 .../Switch_UPS.png => home/devex.png} | Bin docs/{docs/img => home}/icons/brackets.svg | 0 .../favicon.svg => home/icons/conductor.svg} | 0 docs/{docs/img => home}/icons/modular.svg | 0 docs/{docs/img => home}/icons/network.svg | 0 docs/{docs/img => home}/icons/osi.svg | 0 docs/{docs/img => home}/icons/server.svg | 0 docs/{docs/img => home}/icons/shield.svg | 0 docs/{docs/img => home}/icons/wrench.svg | 0 docs/home/redirect.html | 8 + docs/{docs/img => home}/timeline.png | Bin docs/{docs/img => home}/workflow.svg | 0 docs/{docs => }/img/logo.svg | 0 docs/{docs => }/img/netflix-oss.png | Bin docs/{docs => }/index.md | 65 +-- docs/kitchensink.json | 153 ------ docs/mkdocs.yml | 114 ----- docs/{docs => }/resources/contributing.md | 2 +- docs/{docs => }/resources/license.md | 2 +- docs/{docs => }/resources/related.md | 0 docs/theme/main.html | 42 -- docs/theme/toc-sub.html | 14 - docs/theme/toc.html | 7 - main.py | 84 ++++ mkdocs.yml | 139 ++++++ requirements.txt | 3 + 189 files changed, 2323 insertions(+), 3353 deletions(-) create mode 100644 .github/generate_gh_pages.yml create mode 100644 docs/assets/images/favicon.png create mode 100644 docs/css/custom.css create mode 100644 docs/devguide/architecture/PollTimeoutSeconds.png rename docs/{docs/img => devguide/architecture}/ResponseTimeoutSeconds.png (100%) rename docs/{docs/img => devguide/architecture}/TaskFailure.png (100%) rename docs/{docs/img => devguide/architecture}/TimeoutSeconds.png (100%) rename docs/{docs/img => devguide/architecture}/conductor-architecture.png (100%) rename docs/{docs/img => devguide/architecture}/dag_workflow.png (100%) rename docs/{docs/img => devguide/architecture}/dag_workflow2.png (100%) rename docs/{docs/reference-docs => devguide/architecture}/directed-acyclic-graph.md (73%) rename docs/{docs/img => devguide/architecture}/directed_graph.png (100%) rename docs/{docs/architecture/overview.md => devguide/architecture/index.md} (88%) rename docs/{docs/img => devguide/architecture}/overview.png (100%) rename docs/{docs/img => devguide/architecture}/pirate_graph.gif (100%) rename docs/{docs/img => devguide/architecture}/regular_graph.png (100%) rename docs/{docs/img => devguide/architecture}/task_states.png (100%) rename docs/{docs => devguide}/architecture/tasklifecycle.md (54%) rename docs/{docs => devguide/architecture}/technicaldetails.md (94%) rename docs/{docs => devguide}/bestpractices.md (97%) create mode 100644 docs/devguide/concepts/index.md create mode 100644 docs/devguide/concepts/tasks.md rename docs/{docs/gettingstarted/intro.md => devguide/concepts/why.md} (86%) create mode 100644 docs/devguide/concepts/workers.md create mode 100644 docs/devguide/concepts/workflows.md rename docs/{docs => devguide}/faq.md (68%) rename docs/{docs => devguide}/how-tos/Monitoring/Conductor-LogLevel.md (100%) rename docs/{docs => devguide}/how-tos/Tasks/creating-tasks.md (84%) create mode 100644 docs/devguide/how-tos/Tasks/dynamic-vs-switch-tasks.md rename docs/{docs => devguide}/how-tos/Tasks/extending-system-tasks.md (85%) rename docs/{docs => devguide}/how-tos/Tasks/monitoring-task-queues.md (72%) rename docs/{docs => devguide}/how-tos/Tasks/reusing-tasks.md (96%) rename docs/{docs => devguide}/how-tos/Tasks/task-configurations.md (54%) rename docs/{docs => devguide}/how-tos/Tasks/task-inputs.md (93%) rename docs/{docs => devguide}/how-tos/Tasks/task-timeouts.md (95%) rename docs/{docs => devguide}/how-tos/Tasks/updating-tasks.md (85%) rename docs/{docs => devguide}/how-tos/Workers/build-a-golang-task-worker.md (86%) rename docs/{docs => devguide}/how-tos/Workers/build-a-java-task-worker.md (97%) rename docs/{docs => devguide}/how-tos/Workers/build-a-python-task-worker.md (95%) rename docs/{docs => devguide}/how-tos/Workflows/debugging-workflows.md (92%) rename docs/{docs => devguide}/how-tos/Workflows/handling-errors.md (97%) rename docs/{docs => devguide}/how-tos/Workflows/searching-workflows.md (88%) rename docs/{docs => devguide}/how-tos/Workflows/starting-workflows.md (84%) rename docs/{docs => devguide}/how-tos/Workflows/updating-workflows.md (84%) rename docs/{docs => devguide}/how-tos/Workflows/versioning-workflows.md (97%) rename docs/{docs => devguide}/how-tos/Workflows/view-workflow-executions.md (84%) rename docs/{docs/img/tutorial => devguide/how-tos/Workflows}/workflow_debugging.png (100%) rename docs/{docs/img/tutorial => devguide/how-tos/Workflows}/workflow_execution_view.png (100%) rename docs/{docs/img/tutorial => devguide/how-tos/Workflows}/workflow_task_fail.png (100%) rename docs/{docs => devguide}/labs/eventhandlers.md (89%) rename docs/{docs/labs/running-first-workflow.md => devguide/labs/first-workflow.md} (75%) rename docs/{docs => devguide}/labs/img/EventHandlerCycle.png (100%) rename docs/{docs => devguide}/labs/img/bgnr_complete_workflow.png (100%) rename docs/{docs => devguide}/labs/img/bgnr_state_scheduled.png (100%) rename docs/{docs => devguide}/labs/img/bgnr_systask_state.png (100%) create mode 100644 docs/devguide/labs/index.md rename docs/{docs => devguide}/labs/kitchensink.md (91%) rename docs/{docs/img => devguide/labs}/kitchensink.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/metadataWorkflowPost.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/metadataWorkflowRun.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/uiWorkflowDefinition.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/uiWorkflowDefinitionVisual.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/workflowLoaded.png (100%) rename docs/{docs/img/tutorial => devguide/labs}/workflowRunIdCopy.png (100%) rename docs/{docs/img/tutorial => devguide/running}/conductorUI.png (100%) rename docs/{docs/gettingstarted => devguide/running}/docker.md (69%) rename docs/{docs/gettingstarted => devguide/running}/hosted.md (100%) rename docs/{docs/gettingstarted => devguide/running}/source.md (85%) rename docs/{docs/img/tutorial => devguide/running}/swagger.png (100%) delete mode 100644 docs/docs/apispec.md delete mode 100644 docs/docs/configuration/sysoperator.md delete mode 100644 docs/docs/configuration/systask.md delete mode 100644 docs/docs/configuration/taskdef.md delete mode 100644 docs/docs/configuration/workerdef.md delete mode 100644 docs/docs/configuration/workflowdef.md delete mode 100644 docs/docs/css/custom.css delete mode 100644 docs/docs/gettingstarted/basicconcepts.md delete mode 100644 docs/docs/gettingstarted/client.md delete mode 100644 docs/docs/gettingstarted/steps.md delete mode 100644 docs/docs/googleba55068fa3e0e553.html delete mode 100644 docs/docs/how-tos/Tasks/dynamic-vs-switch-tasks.md delete mode 100644 docs/docs/how-tos/Test/testing-workflows.md delete mode 100644 docs/docs/img/logo.png delete mode 100644 docs/docs/img/logo_dark_background.png delete mode 100755 docs/docs/img/netflix.png delete mode 100644 docs/docs/img/task_states.svg delete mode 100644 docs/docs/img/tutorial/SubWorkflow.png delete mode 100644 docs/docs/img/tutorial/Sub_Workflow_Run.png delete mode 100644 docs/docs/img/tutorial/Switch_Default.png delete mode 100644 docs/docs/img/tutorial/Switch_Fedex.png delete mode 100644 docs/docs/img/tutorial/Switch_Workflow.png delete mode 100644 docs/docs/img/tutorial/Terminate_Task.png delete mode 100644 docs/docs/img/tutorial/Terminate_Task_Run.png delete mode 100644 docs/docs/img/tutorial/Terminate_Task_Successful.png delete mode 100644 docs/docs/img/tutorial/conductorConsole.png delete mode 100644 docs/docs/img/tutorial/conductorHome.png delete mode 100644 docs/docs/img/tutorial/conductorUIHome.png delete mode 100644 docs/docs/img/tutorial/dockerHome.png delete mode 100644 docs/docs/img/tutorial/docsVersionDropdown.png delete mode 100644 docs/docs/img/tutorial/elasticSearchHome.png delete mode 100644 docs/docs/img/tutorial/firstWorkerWorkflow.png delete mode 100644 docs/docs/img/tutorial/localeDropdown.png delete mode 100644 docs/docs/img/tutorial/successfulWorkerExecution.png delete mode 100644 docs/docs/labs/beginner.md delete mode 100644 docs/docs/reference-docs/archival-of-workflows.md delete mode 100644 docs/docs/reference-docs/event-task.md delete mode 100644 docs/docs/reference-docs/fork-task.md delete mode 100644 docs/docs/reference-docs/http-task.md delete mode 100644 docs/docs/reference-docs/human-task.md delete mode 100644 docs/docs/reference-docs/inline-task.md delete mode 100644 docs/docs/reference-docs/wait-task.md rename docs/{docs/reference-docs => documentation/advanced}/annotation-processor.md (85%) create mode 100644 docs/documentation/advanced/archival-of-workflows.md rename docs/{docs/reference-docs => documentation/advanced}/azureblob-storage.md (100%) rename docs/{docs => documentation/advanced}/extend.md (87%) rename docs/{docs => documentation/advanced}/externalpayloadstorage.md (97%) rename docs/{docs/configuration => documentation/advanced}/isolationgroups.md (100%) rename docs/{docs/reference-docs => documentation/advanced}/redis.md (100%) create mode 100644 docs/documentation/api/index.md create mode 100644 docs/documentation/api/metadata.md rename docs/{docs/gettingstarted => documentation/api}/startworkflow.md (75%) create mode 100644 docs/documentation/api/task.md rename docs/{docs/configuration => documentation/api}/taskdomains.md (61%) create mode 100644 docs/documentation/api/workflow.md rename docs/{docs/how-tos => documentation/clientsdks}/clojure-sdk.md (96%) rename docs/{docs/how-tos => documentation/clientsdks}/csharp-sdk.md (99%) rename docs/{docs/how-tos => documentation/clientsdks}/go-sdk.md (98%) create mode 100644 docs/documentation/clientsdks/index.md rename docs/{docs/how-tos => documentation/clientsdks}/java-sdk.md (88%) rename docs/{docs/how-tos => documentation/clientsdks}/python-sdk.md (95%) rename docs/{docs => documentation}/configuration/eventhandlers.md (70%) create mode 100644 docs/documentation/configuration/taskdef.md create mode 100644 docs/documentation/configuration/workflowdef/index.md rename docs/{docs/img/tutorial => documentation/configuration/workflowdef/operators}/ShippingWorkflow.png (100%) rename docs/{docs/img/tutorial => documentation/configuration/workflowdef/operators}/ShippingWorkflowRunning.png (100%) rename docs/{docs/img/tutorial => documentation/configuration/workflowdef/operators}/ShippingWorkflowUPS.png (100%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/Switch_Fedex.png (100%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/Terminate_Task.png (100%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/do-while-task.md (86%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/dynamic-fork-task.md (67%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/dynamic-task-diagram.png (100%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/dynamic-task.md (71%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/fork-task-diagram.png (100%) create mode 100644 docs/documentation/configuration/workflowdef/operators/fork-task.md create mode 100644 docs/documentation/configuration/workflowdef/operators/index.md rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/join-task.md (57%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/set-variable-task.md (56%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/start-workflow-task.md (52%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/sub-workflow-task.md (63%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/subworkflow_diagram.png (100%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/switch-task.md (52%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/operators}/terminate-task.md (62%) rename docs/{docs/img => documentation/configuration/workflowdef/operators}/workflow_fork.png (100%) create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/event-task.md create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/http-task.md create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/human-task.md create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/index.md create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/inline-task.md rename docs/{docs/reference-docs => documentation/configuration/workflowdef/systemtasks}/json-jq-transform-task.md (52%) rename docs/{docs/reference-docs => documentation/configuration/workflowdef/systemtasks}/kafka-publish-task.md (84%) create mode 100644 docs/documentation/configuration/workflowdef/systemtasks/wait-task.md rename docs/{docs => documentation}/metrics/client.md (100%) rename docs/{docs => documentation}/metrics/server.md (100%) rename docs/{docs/img/tutorial/Switch_UPS.png => home/devex.png} (100%) rename docs/{docs/img => home}/icons/brackets.svg (100%) rename docs/{docs/img/favicon.svg => home/icons/conductor.svg} (100%) rename docs/{docs/img => home}/icons/modular.svg (100%) rename docs/{docs/img => home}/icons/network.svg (100%) rename docs/{docs/img => home}/icons/osi.svg (100%) rename docs/{docs/img => home}/icons/server.svg (100%) rename docs/{docs/img => home}/icons/shield.svg (100%) rename docs/{docs/img => home}/icons/wrench.svg (100%) create mode 100644 docs/home/redirect.html rename docs/{docs/img => home}/timeline.png (100%) rename docs/{docs/img => home}/workflow.svg (100%) rename docs/{docs => }/img/logo.svg (100%) rename docs/{docs => }/img/netflix-oss.png (100%) rename docs/{docs => }/index.md (73%) delete mode 100644 docs/kitchensink.json delete mode 100644 docs/mkdocs.yml rename docs/{docs => }/resources/contributing.md (98%) rename docs/{docs => }/resources/license.md (94%) rename docs/{docs => }/resources/related.md (100%) delete mode 100644 docs/theme/main.html delete mode 100644 docs/theme/toc-sub.html delete mode 100644 docs/theme/toc.html create mode 100644 main.py create mode 100644 mkdocs.yml create mode 100644 requirements.txt diff --git a/.github/generate_gh_pages.yml b/.github/generate_gh_pages.yml new file mode 100644 index 000000000..06b0d0894 --- /dev/null +++ b/.github/generate_gh_pages.yml @@ -0,0 +1,20 @@ +name: Publish docs via GitHub Pages +on: + push: + branches: + - main + +jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v2 + + - name: Deploy docs + uses: mhausenblas/mkdocs-deploy-gh-pages@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONFIG_FILE: mkdocs.yml + REQUIREMENTS: requirements.txt \ No newline at end of file diff --git a/docs/assets/images/favicon.png b/docs/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8a0f42d2649e3af1d7f5ead7f75e049418756ced GIT binary patch literal 20196 zcmY&=1z1#3*Y3L(hY)ypx%Su z_y6}^eCC-mXV%&A?zP^v*4}H3(o|Q%$DzRifk5~w%JSME5V#0<)x)rWJ0Tf5mB1U= zOIztNsB(;M7x?hX#z4hZO%3z_xQ2l+!6YDzI}zX?2uuUQ{C5okJpt4H@3l6V>e@Rb$HmZ};4l4ZT1h zT++K27?hDk1+-@9pljf5p!QV4%FTt>!rIN!hS%T4{q8A{l)nUU>0;w;0rz)tcJ-3* zmuC7WApu<9-R5J0{}b_cl4dec(}c^pdD_55dHH$ynPhO_aJZDGwXKA}QXH+%xT{Cxl0Ht?v_ z-CYR{PX`;I=ezc01f>2+{(txW+m96AUFZMLVE!}He|LeY%HT-x{V!}XIMim}ut6XM zNJU;&*B^WsqUO7#SSv!pnw&E)H|&N8TvB3cfJ|}m2Vs_*p>|Q{fm=VydGIsZA(Z;& zfta=xh@xL@HG{IMIUyDA6v`5xLy=4|*5ofp-+(tNyjY%rqecd3??IW?$K|g;U@)}f z>ZbG3E&Sc$;oRI|=IY1Jw`VM(e{D#V&aOKD2K-F-`nnPN5I$iAeVX8ay&Fw5ClmPl z`hKp=f{02j?K^+TTHo}6o2);F986zWIfaf!!t!;13VrzWL3f=|Om;1r06_!fCFCt{?#5iY(u zGzu7CqI0L@F5jO&fs1oWO8pB0!uhRp1HYNRktux6oE<#>aW(&Pv%gOnfgpqsDF^y9 zst;0*ug1nj^$VZmT&+>L^FX|Ha;PLF`qNg7BHb)dVqmQ`o(T0-tiD)bIADtmKQ4e!c+F z4mHrBw3Pv=XJ@d701ECHn$_aOt866^+FumU2Ol5?zK%yu&#Bomf11fsT&XW|pjezF zc7DpE)w{U|Af){{=>qCORHkA;)NPnAoe>z6mw9Qu9{2)UCDkqkvH1zee2hrnn{j6U zRef#$IAEhgPmLqeD;%`kbc_`B&!sVRLSc!mN|1ufnZaR_xdrWm;uF zq7%_24w*>Il3Q?za9C-!_I^^B2bFKsBrNc2>DE~32$o>zwdRlNW(J|AnJMfhSZrqik{r9#GyC_t?7+t!Z7AOr zx44jmPF-ZZ#$deSYdLC!5EbGccr2L!Gh7><0T3ycL555=$D1x@VYZ5-G``mB+Idl>^>KZ=ION(q5_R^f=6ClQ9tH<@x* z5U4yc5<&9%=9@X0?D*@iYYV@LXy{In$heV9Sk6z7RQOZkuC(mWue+#Qdp;gHR&lKN zvdXFZ1dUx)Z6%1DZujO2FB2?tv@#aE)Ye@8x_l`m{$=F;gs2pt!&0XytB>QNUi=Ua zF6o&nD5zbS3}EvfX|5?8{@eCzIG`j9$7k%GYhm-UXwCfS>#sQz69ql|gvtILF&R$^ zs<9tYL6ag_yHt29-sR%&#Vsm!%J=8=T};kvzS8(w-2HfHZU+gSmN6A7I@GtmSJeWW z7`q2(k-B;Lipo|mQ8L;b{qcEmJ?lM2l8+_1f5aatIE2M28Mc4CLS9L}XKtrdKSv_B zv!@o_BCTcN_q=|do{5!MBd!(w4I+AK=s6@-+SK0@J?^WnWy|UvoHLL-suOi~Id5Ej zE}cy=PGN^a1Oc;Lurg@Iu5^%h8~a-xjH(P567<$E->DoYn~DB=W$kSqn|-b&^gg0D zs21ZW0!?n5`=zOX2N%QEjdN^4@S4Ll8ONVOa3T={+Vsy;Xvi4X(CeZuy$ z&BT9YV-OtE>HZ39v?HOV@3x(g4%5a?A5}`0=__EADZTL_{sALWruXX9H}=^OX}sPJ z#RG73kM)7VeO~Uwr2|%6^6Y6J?|?T(-K*$`@2{(tK2HnCgVAdmGyW!KgdSYaP1%1YZbYA7M zaCmuwL0#5>!@N>Z`0`~RS~W<kfu$f;r`i zuU-$x70EVWL0Z_ceoL&a23~2<&&v|~V@1X>V=Zz@2*8wML=xX#drT^4CVJ6vD-a*Xocm}IJKS@_ZcEs6=*U;wv9q? z0y`TZA9uFd{q6k zGoa3y&d^7HIy)0xQ&9?juwufVq}l?OLs-t=J@HE+1o=aX(N0{<$&8WiRJt6R+`Jq|AnoQ$ODj_0`eWFJZQnt7c#SS|&7lot_^u)lRa0 zEtSyteuWo0L`s4&oLtvN&GiD8ck zrJ-PLEJ693w-q8iYwjUXUM8!t?<_eNA%6LPrhh*Nqo$Lv4Z9n5O462_+Ds~xmMLGp zY*!QeHK)y_FC~8uC2XwN`U)NJ{$!SylIWKa+`Nj3Gh4xhZtvMLny@v8<@8YUuco*mGVG+*3H;-yVsO!}T5cms z*hQ!s{{8T2a$*Snr=9L6%xb4X$wex<_p{2p*6tjiP1}J_Kwx*xu!oB%xrO! z!JQ1^S%wjaO07ys)7`6npU1|2){&o;2*6IAn?4CHQeug6aFMPz7fAu*W_=N_h}j#Z zeyHF%Dcqtz4CZW+CWKoxR`T_BqZJL&iQxw42ZD8}3+WFHsB@peCeHA3C1CbmXTBCM zKM4KZM3<(2W4GqgF5=<+GTV845lu&iUYx4n;jKIj4)kqbsE(0YgW;EArC)*RGLk*d~4LsQ|M!o8s0j;YWSO+Rx*`>NtpX*=3me8NAA}weDcNxR%IoC`Jh@ z@)_9peAb(4%sSeuekS^K@w=-})bFm!Wq0!rxW@Yzz56o^+0EG=GZ|Fg6C~iepQv{2 zQ_i~p2E_29(4wj&lx?bnuvX1kTE}VHx8Sl6i;~fuyL`P8hAHMq+{l9dvJo?m) zcEut~MKf8T+F;j>h4Kf_v*Y6IgH>z|#Qt(4VI>kuU3AR@UP=nvf4+*BQsvQsJq=_t z@_+Kn!YjJjesaVV^oW#f66(x5If%IlmTPRXM-!(Lo8QP~#j*{5&Iq<^WnSvagm3@J zcvw@-UV$QqIN-4{ntWH%1d(Ekqi-@-gLm&s|hx9L7h*$%{BQa^RJS zy%=;Sb`qtNceR6{ykq)C8<$!gj<=B8d|6fMQ=`TfMAdc#SNC(0{L~k;Pl4!JXK%g0 zB)Tno4f(9}qU&G@}zX?5@%XJMgevMawBKGA>R@ zZtR=3S>xf~hZP0UjiNt}%(VBcyfAW-UvB$S!RY@rVBK%BtAB1L;7D7NWu7^vzgt`Y zn$>05L9L>48s)Z4BuxIy>D?XjCUTUFmK_=W|J3sJ;!QyJNhf9<*LL;PiBWmGl%!thx! zA%s&Yy78kj75y{J%6;FBf*_{dtqOivlT&2SVka)`{h{oAi_VyIJn1?IrF&5A_*^y& z=Fc%Kr@>S44L>Vw#dOscETs1^oYX{RC|%c_bMf%R8@~9QCguBjion7UOx2#p#qBgr zK1M*4qOC7e_Bl^3kJUo^!+;cqp6mo(%W0KchtLZ)z8;9vTpMZ zLM;8h@?ud-O2$O|<5`%*B~Joxe(Y`^zXh`@4qnRR@l!lzq{cB$3^i!hOyq!d84XIN1KNb?7GPV&0u~U=&F0$LYkW`Ul zn++`ZK~wc}^82xu&bWh*^nQ+cm#$5FqEyj5cCreR|;O;Gv#sR#C-yH5F(rUb2Y~! z858t{0?KW=UrI%vWeV@gfRLAFalhcY%EXF&GMQf*X|c6Ltlm9ho<&pN9H3Os^T|V0 zTa4N%Lf33Pi?V6@E$g~B#jSxLue!cGTwd%1Q#c{FQ@l8@$&I2j6cDS)6=DlTNrRE<}z!MRFXzwNe;JaR?mn@9Mh5Fu{ z`-5G*_cb#*Ft;EV*QfZiTNk76u#B{62L5C&pfF)W-@kd=RIRloLv$`Bde<DhsFvasigsmD34&IxN1#dK;ER(YIT! zD~_SRnOaI&8hUE6>BcCT0Tf!dC>u$nTiJ^_i^k|bMpF@VE?wtglGw52wQtK!E*9JK z)Ba{U+^XP#UEO8m#PSbI2*ERC!t*)?I*E@}o)gP?u{5}FzCQi&FpNSu0}mA}SC{Wi zeOuXCm)XcvCE;nL-QcqzavQdrEAl!KL(xIss^Imz;74^~Nfd!5Uu4GzEnfWi>?8iMnVW~y&`74blm%d+4%in6IEeh`qf27jzqQ-vq zZ|b-*{2hk#?>4t_d!hJM?Ql0e;zaaIipjnV$YMmyLX*_z_=g#29?qb?-p$+ka}i|} zS)d}s_iTo2`m<~o2Uq@Pnwz?k<6jm@`!j|mvq2i%#i-?rmX^j5Vx=+PhKy)QB}pzz ze><{o_rpk)-*s!GR#>tfZ&k3X@^|Vz!3N{5%E{tWdUn}wC0bS0^Jmegg>PZW{70$l zWmBfw6W3qOPkDZER~K#yaQdbgBq59yyOocB$gZD{trjmnkGA37aJX71j^nG~W z<$4dc)HR$`(vX9WsQbiEs)$@2dgR=@9cUJO@O8_lmh0ztQcvQZ%44Lg1Aul&n;vWS z8~C}AhUqHz&LcVG{oAZkQ$mJXaN)uaen&5J$2wjt=EQpPoa8a(L>fPluYn{45LYkK zwS`}>S9JU|KwH1KidV?Ph?Lu269L$$$#%6ti^GunJVEuoC?uO2>X0)DT=*-RYR|*k zu?~&JnD}^HIdS`9!KseO#B5l$%l*tZ>cd!vfsVOkDaGDhZo)_iffg^O0os&NF_N!h z{)3)iG6>b{x*{sL>KCcCTxsX_Tt>PgNGtOqBNQpLXz00QE7OF21mnP?A~y~?HanUy z2wdtK#BiVT6FXVKWpTlh-gSuu^y26Cr1aA#LgS)(;6lkH*EL2;fdfm0FSsr*A1y4k z6$93=NK~xRyz$$M)kNOVlQlMxjd{m&n{OqHc*PMt!l^c^AF?cX+z>ABx?;H(FpocG ztT#n{k8CC|BjwwxvmSZ)22#rN)yom{yYtfBTzd^Z+aPG3tr`!m#j;r_i1 z(m^8)wcTEyWq9JEn%(lbS*JdlOZ6+hr3=e6Q}|DDonQq%OcFP}8gP}rW0KvMSZKjQ z1{%J`lEd{qg5_9X1_JiU_+%nT>))h+M|taa+L#XEJdcIagduNQ><+)PJ-$y3*`>)F zIdVMVpA`GgJiYtQR_+z);uAh(mi9g;UwK`bi4Ce8p4PjyEV_nyHvPPujDl? z24Hp%z)TVt_JX5yjezlkQkyS}B(sBzM1fuQ0}6Pi<+WzbA%)!6`z}usJPE|yC{^!= zct;@yT-N{+)p6da6aH(Kj^{V+S0!9DAw=R%RLx{Trh*NYG&#C|7&e+ z0vA=p&)lsw|J^S@#_#MI=^d31vZn2*65NeOXFDDlLnUfzvxiAiHqU6sveuXZsEdf{ zQq$NYNI9o{ZHe+iz8tURocCrarE-#^FLh+Fm5Lq9#It>seU zRS$tyk^)|4xlCaH-tUuVsREN4MjRW#dT!5;o)+zVgPTXU{!(*n!&!%C`e7zcY%oHA zO&IZ#!UyPVFWMcn?roN+n0s{UFC(WFl?EC>5#DtPk$k{~#BZ-hB+$@-A)EhWdp}Y= z8ebdz^MR}w#nZIpbsXc}4SwmS3@^3f1vkphB43V2Y>9bJJyoxN8iuZtAI`30PE2bi zr7uvx+aED5Mhs2$mwBG$47LWaf02kisO53#4h392_-EHLbPWZdP(ZaxyW7>Y1tW`H!;w z9%H@ptsi9+AN?~?(Ft+IJKq{9Y-|SkR@#GGxI|vY3OPglLtf5B4tC29!u~aA_V`;t4w6pana$I!^}An~lCZzw-f<0Lcl`ef{TZiyFzBW=I88 zM^^-~!9Am`2N&Jf?_uum{n6v1?JL(lpEk$lwFa)~X#DKaf=VCz=^wUgzD8L3%qwmM z+0{9&PI9k4vtQNeV_@-ePECGQyRk`?uV4%X^d{ueGeTHU+I8(5QprTygo9!!<<)PF zD#A|yY!EOV{crac@CZ>Obv#Q*Ewuo~fW;p@TZK`_dRxnf#+jL7v~;V#uQrsU1n2N zWzT!@!`KG%+PD12gFBAyT3{?4iqNBomZudJ>=`wNIK`!lInb|Bj$IiWzNr7VtbvWQzu`YeS^bY z8hPZN34VD2(8%#wcP7FTgNAnMh;(DW(L3-~3g$mP8+uItW=h+3Jto*`h?8v)JGPh->=5Ki zOfr@67#QI@!UBkju_8p2#dh+$g6hB*clkoP zc^CC+%EIx|_lv0oUp}P(84w|$xX)`h0;h_s7xF9f+CE8=vxhxKEL0j}RTeEvE;eZ0 z@mX~d9bGRp>C-2#7OoQ^qHDIAQ{lz-`#cn{ca5ho`(xo+Z-z#Fmo|uZ`HO14W0TaF3o-U zL|m)%GPRwfsqh0Se$=Zs_Ai>49x795bx6#|LUF>73DNwY#NhyrkMXH&{@8md?-fMm zd!bTj+or8q;Gztw?cFB@-?H?JsOf+Bk>CGjr14-2j|nRViAHMedTvj4#ZXC*E4S(O zVev`(g|#Q~O0mB?`-o>ZL(s4Z^4m0EnhkO$$hF7tk~X)VV@d^jH)ZHv3T#k^6qarE4d|7Ox>P zpS01<#dXE`D(G^ZRA5|ihJZ7BKwMnNKC1Px;g1_X{uhKKU^UmZW~F0v3hCGNZ8vph z1Mdc@1^Lr#$5+OGns_LJ1stMvu(Z!ARq}FFgBo^lt_fWwV zQ3Ik4&zr4SsPNACVOnBqnVR*>#HYl)imD6je@2(_j4q*K$1_9)VT^DDb=kL@6POS=e5#7j zuSC&QGZe}TIO9ODqO3~Eg`>C*Uqg4>>rqH9fKW2<#Ev&Q;Xyq0wf&zieD4L;wxtF> z`8$zyxtAtc#hNJiH_+5(aC5mWxwQBDM?oyNd&+<(%Exs3LhwwJytdV;rkk3@8gRtYxXWz)t)u%h4dDVd%X2ncPqrgCntGy`IRUB$KxM z*}$Y;8Y?jP9-b}xDqqo6Y^O;{^Io?hiQ3O?K-pc-e6q<&aVsg zyg7S1eCur8G5-8klAZIbVxVTPI}km_sbgms>N+5WnY%rT-^bIaY~?&)e)noJXcxXF znBmRtQGk|0?zDAXLE9$0>RwL+#PdOpYdX2+QiP-6*>-D&t1fgCU4D zUWkKjuv!L0k08+-W7h1h6G1kbRhz=b=R$)O4`%ShB`X@|WvC1~X1-vMxPxGLc;JwX z&@628oc8aJ2~|fYYiX}AP(_39WEY6isi_-StXMS2oDxTk$Fnh>V1*+4k(1>~NW89$ z7%CYeWwt22x%eU>66?^12Syr#jTnkx$RFYf3jQYy$c8X_r0aju#t^ zuk>-jF(k9;1=S5(gX zvYDC{QlRYdphytW4h%($>aFyz1A*b1pEnITCgpSRe=j1e->zDZ;hLwqC^TXNj>M?y!^@e{b7QrFVS7FboBDN2;Kh z*r>Zc8Hk(6m9^^E-^6o7jp{nyK9!6nDDLN?kMMM2d~VA5#2y4ic6AXeV0#wp;XB;go>h ziJViR<}$9)8o!h#cTAjvGwCAZ^!O9KD@q{R?l^b7FzlnL(<~Dnp z#VVwI^_{05^R9tu1*CA%)maN=v!lkMkLSW9(oQ_rqetB|&~o7T+d z9j1%(Zy2MtlU;2i%>H%JgXr2%%hnCwzvj4JVsHI0Gp*}bDYG3_^Y2?1qLT-S3q)qy zM#(H@he|WSQk8Oi3lK2S_+$C55=2WPBnv$f?5IpR5nuE$&f{*;gdj4QQ!j7uOl%h7 z#LJc_@c*?0W?s{QV}e_2RH#D5V6K))08ZX_6U$2kf*<1%E!1y9Dll!1_RiK=WfywMR0^vz3hZ{s*cP&cxsr%VB|@g^=cM zev*I6)(N{JQ?_j0>!$v? zZY^x+$ME^zbP@3ArrTGI5e@EBIyFlHWfTJQ<}?zBtqM%f%hF-fiF!wF(T5u?~~nP0n}jtP=|G_^ko#X#&gk^ zb~cKQvHI+)ukVe}UVj^$>=J;V|yU2#EjWl6+bi?PM!mhoZ8fV2MgrfDQe)5nI zibTL*MUdUQLZ&lG!#dVxm-ODVsMYJ0_%Y+O`=a+t^S9FkgE1h$LJlw`&L`Fiw2OGv0L@DELv!t#aR|7ZWB|ZQ{B(a zn3$;N4Cdbd7VJJ$S+?o7wSd4@Zlh9gaGgG|8|$nZe+wM`c+t2id5-v4)$}Va?D_r) zt*)ew#{fnN0{gNRq*z*En~Lv-%DES{<;>giIk0hJaMW!5nDk_(WiPQdx#Uh9p}|{Sqczn5OPsAP@09gn+_b&3Nx!qRZ&*ST72g>%! zz2w)#?15%JW1j^cjGoO((vXnm%Nn2Lvr4`Avn5<`>nTH3<2V8ov|OtPw?^~QVL;?$ zk=c>;ZISQa(wtTKJ(3|7V6@Kowv+W7nR?=NcjWh*mUfCq%Qx_5d0Y%jMZ#b@eoKiP z_Iu>&+lQ!kF%FT0N}q}0{J~IRapKcDyR)9pQDl22E{He$(L0z@+o;akrJ-94@(6y@mO}6rU+?Nv)g;rUXSPhC+Swg7xh55yy zOS*uugn_VX+s7g)Oj(4A+nepN!;l0ZH16#W9%C%Uj-Gy_dSGLqlZslBN_JElNdO`k zLZhrjur(4JVIdZXdt7JK`>|dnTWJAnx@_%C9!ks{R|($)oYVZs}f1AK&H2y?En& z#-<@EpjJ+274-9BbIiJV`3Wk;5vTuYI_ELN}|97$Ght#F{C6Ujv zFdz%W6I8q^j5*3YDYYKJ2?5i%=|78# zCcgU7mx61suf zfCOIhd+WGE`i;iIm)iJId}%2=3ca@58%|giLS3m}7SwRZWC-4zEmth z+zoZz1m?hw$43jbKF}JFsl66FG1nUF9d>(YL{&bhS&B(I0fth*imvxKC^x=0{_VVY z0+d!PfXne#KZ3hbF|~KP3mh(Rh6aqW+Li*Y@hCjKdXy1)^bkV=Wog%y13K2tt>tad z+7Kiv9sBs@-V}*7ru?(MX9=`T-dHid2{03Us5Q}m=cB#T_+rEHfj8fDZ7=Et=GH-L zMi9=$zq;hy56pz@_5QGyzV>}dcaigk*V;c93!wc4q6wp5bx27EHYoX%&R@BX!}JY< zYQ4Whyp`4ZK!J8;io}S}#Iqwh*EQ2}uJd^%=wsky&?v2KOyXlPBag%k8TU}AR*S8s z9>y}Hs`9Vnu+Ge;gQ_AR^J*T?>v!aO%<{gT>`0kbcRN^>AM2?Fo_UxEoRe3Y$!Fy91Y}RcE-HLIw|WAwcMfPmD11`wKO>$ey89q4d6s3UPR zn#O_qK0YGYOnFBZH;xMuu}ZYoFxpH;D6y1TM@ z)T-@1g_o95%agmZI$_m>DeX{mlS$JdhPsG%tWL%CxhE+=Bk?YkQbF1GSg<6|Gh`P8 z4HCIN*fYxMi_!f8Llj`=4Ob?USs!z(Mm3E=?$0pu_wb?@#mSYqS>*^Rd@Sa;Yz=?b z9bi?Co)~5a)#5+!>&0p(E{k(U;UG=}W|Bwb&b1Zc&S8<-hpj2R986`fWM^3U~ zUPc`u%|dVT>>^!PEq!p`djTcpGNVC1Y3NVLQTy z!R0rxz~#j5n*%}Cg;uyK9mvB4q81#nRE{O>9NQQG^k03#UpgEOTiGBQ_>S|lXt&_q zxb@v86x~7}^XZX>KDZ0OXBzMOT)Dr=h*5Lh!5Z9jK-1Ni%3%kFmw#oV!{G1*uN4n; z)LUu+mg;=tG%U|$c619ApnQ^x`xxiB9rHD+Gej0)+#wNYpQ9Q1*#PqBMY#6JSJqC= zC$ah#vA9FlEAOrgh47=;`_W`8b`|_8`D_eB*`Lp9CN>`67mo59McE7E(4g|ka-BZ) zPC$^70v&TH!okGvF&Q-wUwR?>~!ENP}JA1B>Z#|X^+wt26L9rJ_=Yj)J{tA|?n7rpau z2L6CCwh#0Ni;5|GFm8a%`wdLs4rSE6H`9hjB=1-w@F)ZyTBW>TN|anYu+LwyIc=^L zud!9;8;r0yEE=n0Hp)oXbwto8nZQq%ST3!~%sJbik8X}#kCx%MkhpkLYqHwL3J5cG z^#}W+C*?BRJq?2^oy`^?NLG;^Oi?qawqsqMa}6$2+h5kq_f&hQ%?O$ZwM&xrNjDYWY*k~ z0K_$O3a8CJ&iLY>D;YK*@5W@zZ$%X@U0brQxqX1`c%3O4dOmlW%nYx8TaC_;ZA)m+ zQ~S>6lB^~SlmiKtKUqrym@itdv-q;E zAyrbWpJ{yg<7>TEBHXaheJe5tPYk<`JYZ%%->aOUO;>cq6@iB>tNp*{chdc; zau|d=I{IJx&s1Qmz7Fr*r~wwFyPyL<`Z7IAG;BZQ*GX$9pfLFZi+V}oY;pu3UgH`^ z0IvKZ)~qjnco6D!md8S?$sQou0_2TPxb{aEwN*3;jg_CE(A7vP;KC(?xxx3t)iP}MB6HlLP9?t zML-S@Cyaj#NepYg%0ky}lv6vZlD}7npSBO&G`E?)-|R?PIUWMqP?b~1t|8Mp_^f9jt zr6k=g<{AbN z{#t4)#C@R3Ya7m?w1a;A1V3s})rIq!P7Uw1xt0r-(k>oF<1pFkuXx0O%^-kXWvlB~ z9F`pAj!wxk$^$lHM$I67tev_(f9RdorXpZ9MyM#-+B1AMrT{L z(B`|2UpyG5H@s}IJuId#i2ks#4b!LOa9qF13PknJ+Zlem!EzLk={!_+ym!k+3GgT8 z#ns}YJ1|OWxSK`Aj&Hb(+7XtafTJ62laSIdU*gOz~P2_Rd-bl!M-C4@1r{NUl=L=5C& zx#|M`{y6)&=k~wKJPGnu`)^dsYvaL`=To7WJ54c2Q871OnaiWk>`H#62V0a*;nq$v zc9~vc%+p6cimod+4OKtfW%&SIr3ql7XBy1~tFn)R0$%W9o94&l!xPDKzmQC* zmkuyVzVSS>9ZjUqtv^Y6b*hrhSRX!ti z@cw;sVoX)M-ZX6`{B-pbn)S^zaPUk)jJYb7@zL} z$&@61)OO!ZcN?FHM?7S78WdX85lWI(3#s!oPzB5hxD>K+F$ zoOGv(bebs$a$>nl>nON}>cks=w^ZkzZCk0Ke_G zpE{@u#cr>6RCbsFwOv4UkW*(Wub=vhhyt@0thlCai5B;yAMzj(1AW_`5$~nJZ_Dsy z37&_bH!qCf0ud~iQDx<@UGqt9uNolMNRh0neG>kK&FflUFYWemEq4F_f@%48pls{~ zElm2Ku9z-S?bOjg7%6iBGHY7c`>^`)5)xH1(p_*6X!7#cvI%2*F2kL_Z;CV<{s$xw zYS}my?p^~0n$vhwdIO1+h3Kc_YHma(jSS%3!`+`L6M>}vF>>Pj3puo}5SB#3X_Dg? zvx~JK7JfQ(d_)3dAa}3AgptVew0YViiteq6#eB^DbK|I&q~ag1BnaFCn6NBpO|4W-ofA8h{m07%t@Z64yi=Q6JMe_>vR5L^)RKy_*(c>x-hp*^BCLMxH zja2mr%O#PFiLgFX;gd1~$U>IZfU%cJ#2--jF%d?vU%&^yL5?p=<5|gY)pLm<<$Ly zz(!Xd18994;WnH!i*h;vOcZZ0WV`R}{vTVT$K0M)JoXc5-H(lBsxj5Cf`B@bzU{{Z zpbk^;kJV6Oe=}1)(3yAn9Mh)#bkHtR%|mk2reaK z$;Uc)OFrcKm>&`~8^7_#E4AT4`=i{=c5Q%f@cP}|Kh$blFBbI&56N;!I)1aKG>q^6 zBs!)ZP6s-`>kX&JWKEo!76I5+VbbiE=uBdO51&=ohlUIW;UCo8dzKWIK_PwH8(E&f z=w_;C!aN)ekfUxdTRgSrxdLw%%_i7S^^J2RA3jueZ(pD?364Jqsw!)suvh5YUTF^u zg!5~^vI~O_^;toS83o8n(})JsUUAr>=94O^i#WqDt>g&1IMGMZ`ccz{E@Ny-ecLk5 zv14QbU*AMAGN_)K4nOP$vN)O5glDAO4@`xWO|fD)7QRJ*UnQ-6ehgq{f28yHlGcb;K;u1E7nMKwfM@8CaGM#xVef2 zX*uG_a+kwPBb=RDkE`&t-#4|sm1A5Z=G{7r-^}GVTD|%<_kO=J2#%+xn#&p4uawY+ z@C=?lm|n2x78l{>V}C)*KD)n~mJslVmm#i#GptYWqMm=1HDcHs1j38F`@aB;t={|x$r34I}@jbL&9&0TEY;8T%P&qzGWcp)f_tUEW)1taLVX! zxgLA=bLYsJCGqLX-J`%vvk5W74km`GXGd0<8RQzbQ-<)89mR%8_dK?WY{xbHYJ z`5@0B9Vhn&wu3{_@NbuCqafrzHGGKqkUwi;L{u=ycht#(M_71$s9efHNtO;w1d^52z)>)_M2nYm%0N*#UeFVEv1%4A&_*6mQ@ryTS)NZdz zTZuMQGU6-g{8wCJ-J1Cs3q^@JVhkYAcLZ1%89UlX)#GZMgoacNr$}4PAS%5+tw_4F zW~t{P-CWR=oI#3?28;lNQGMn@AXUUH)kbvH4BC@+181lplqxP5Gz%I52%~1~hd`=< zo3@%kCP}6>$amJ1g%Y{MZ&QbGbZ~|$IvNfF_6pl{&A17HR0B6Q?k6@<jU{0djCSr)zfVUe`r?-Wp(Sg#|B7HxpQU=g4#eZ(2H<1RF$2KZ5yUNu@0d%Rt5$mY4b4;ja5QJSG=QH0`t z_p!n65D>6e^i22~!l*s3A&?qi$mPy8ZB>y_I%JpZ22*i%32#yGRt2(zJA)J*^#K9W zig?|p&xAhEtDHt`*)?R)7k_`f9Ver6MKL)S?_NIjDwGybGB&we`rhIg{sMu4Au!{L z0I(Ig=~r-ZN*x*me5DTugE8v-*ftOt## z8@50owZN5g%6-^~&&i;_)uJ1xm$EHK+glq8rZ}-mzR^<>3rX(*WJ3wWZRrE3#PFqA-;}Y91k4o9GL*Bny8~yF~S5K$j38_E;Z+-sp>7(5^N%pNZyOD{u=`skZ3fQ4Z zD+DgcTlUfzd!EGc*{6SeJlD9h&}xP~yBGq=5yQTG@n%X}QHPGYbJC4Qds5wr`N-E- zmqxVj7XlDGdie!`7FOse>>x6i80tA3Sa0oy{a?End>vpNogG{aGon!dX9S{HlJw^Z;l4ItX zNKc--rK#2c+Ty z1b_e#P=f#jQb(-NR_=UxnyFBVqMv_-+=%S00IjH;AOW!qJzIZsiZoi+;<0v zQ14&?pi3YC1Zp7wfm92w`i-^axmz^@S_JRu_&9gSSk(l_fB+Bx0-YlOfz&a?>_Cv1 z`Rk5{anrqxe%btUK8CjB22S(>1b{$21Ykp|hga>^?BLLwvfd{8hSMK$NsZbQ#RWhB z2mpbW2tY$>31HRBY3uHs+V&}f^#xVNJVIs6svJf;AOHl~A^;7kErJ!Vr`V)%#im-C z?NIsgU)ctRjN4jR0y==eun~ZUB*@>Mc-^f-1SOo;AyaakE9IIbKA@ZJ^OB}2Pbbz~ z_NBI#N&rDrWdtCQDnsR_;p69TPN@LhH*R|K(F>Jzj-unu>tqV&hhqF$vy2nVCEE{+ zTk1YbTen|h>sB|9hC&?#k}xN^6vT&(nCwYrOL%Da!;x1WJ#0@u{d0Z_;grFfdR4~B zII;<4!stJb*&*lc%bkh)#e*J!z(WKekRD>v`4N6-aZWjUn5!w~*Lu<{6MLU7F)Av8zv>lXX@XYG~C#3vHbs0D9#GZ`2j z2&4#D8~qK!2#diN0??3rA-^ zgb{&~0bvv<tBpWYyl*Vj3vR71nb65bp1drT;8`)>X@ zAEQJ_k9imf=@3XgV{}%1baYB&BdbHtc;|;)``x>jJI8*;#;8D`9s&?Z_3%>DI=oEb zYM}24A@<6~@fej}d%`Y|nIZsz)Z>MKExaBxuS?RgMb{;|=(|G%Ador)SEB1QUg?w3 zl&pc@NQ{Ru^ddkv^LwIhOinPorHc;`=p6zONWEj(YJ$l%{D97IZ6!E7tcGaVN1+uB zo46~efIxo`fI#XGuNqVO<%^3638f?0ml{K;<-(bb%>=I8TG# z{jq)NdLur9U{cDUVzMBJit)kOfgk{ZG!RBsodyf0KRy{9U@NaGVjkP2TXW_Z8j(jH zU}#YUAdp1i)-_mWV~WN^6`UWGu!e3(jwL#!M5wei2OE(Rwtx&F1R#)vfYv-rnjgAdu=J7D)SrttmFqK24bg6f!`#;r&Hq%vqeoQ`9DM11NeK4gwHJ z!{KG8GszOUCi^xr`!vvAr-KcsbGpIa(^lMBWbB!ZlT&Cy17a?w;v4}8Boo%ZA0xgu6D4(3Jpcdz07*qoM6N<$f_APyb^rhX literal 0 HcmV?d00001 diff --git a/docs/css/custom.css b/docs/css/custom.css new file mode 100644 index 000000000..e9c4fed86 --- /dev/null +++ b/docs/css/custom.css @@ -0,0 +1,205 @@ +:root { + /*--main-text-color: #212121;*/ + --md-primary-fg-color: #1976d2; + --brand-blue: #1976d2; + --brand-dark-blue: #242A36; + --caption-color: #4f4f4f; + --brand-lt-blue: #f0f5fb; + --brand-gray: rgb(118, 118, 118); + --brand-lt-gray: rgb(203,204,207); + --brand-red: #e50914; +} + +/* Grid */ +.row { + display: flex; + flex-direction: row; +} +.col-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; +} +.col-6 { + flex: 0 0 50%; + max-width: 50%; +} + + + +/* Navbar */ +.md-header { + background-color: white !important; + color: var(--brand-dark-blue); +} +.md-header__title { + visibility: hidden; +} +.md-logo img{ + height: 38px !important; +} +.home { + margin-bottom: -1.2rem !important; +} +.md-search__form { + transition: none !important; +} +.md-search__input:hover { + background-color: #00000042 !important; +} +.md-search__input.focus-visible:hover { + background-color: #fff !important; +} + +/* Fonts */ +body { + color: var(--brand-dark-blue); + font-family: "Roboto", sans-serif !important; + font-weight: 400 !important; +} + +.md-content h1 { + font-family: "Inter", sans-serif !important; + color: var(--brand-dark-blue) !important; + font-size: 32px !important; + font-weight: 700 !important; +} + +.md-content h2 { + font-family: "Inter", sans-serif !important; + color: var(--brand-dark-blue) !important; + font-size: 24px !important; + font-weight: 700 !important; +} + +.md-content h3 { + font-family: "Roboto", sans-serif !important; + color: var(--brand-dark-blue) !important; + font-size: 20px !important; + font-weight: 500 !important; +} + +.md-content h4 { + font-family: "Roboto", sans-serif !important; + color: var(--brand-dark-blue) !important; + font-size: 18px !important; + font-weight: 400 !important; +} + +.btn { + font-family: "Roboto", sans-serif; + font-size: 14px; + border-radius: 0.25rem; +} +.btn-primary { + background: #1976D2; + border: none; + color: white !important; +} + +.hero { + padding-top: 100px; + padding-bottom: 100px; +} + +.hero .heading { + font-size: 56px; + font-weight: 900; + line-height: 68px; +} + +.hero .btn { + font-size: 16px; + padding: 10px 20px; +} + +.hero .illustration { + margin-left: 35px; +} + + +.bullets .heading, .module .heading { + font-family: "Inter", sans-serif; + font-size: 26px; + font-weight: 700; +} +.bullets .row { + margin-bottom: 60px; +} +.bullets .caption { + padding-top: 10px; + padding-right: 30px; +} +.icon { + height: 25px !important; + margin-right: 5px; + vertical-align: -3px; +} + +.caption { + font-weight: 400; + font-size: 17px; + line-height: 24px; + color: var(--caption-color); +} + +.module { + margin-top: 80px; + margin-bottom: 80px; + padding-top: 50px; + padding-bottom: 50px; +} + +.module .caption { + padding-top: 10px; + padding-right: 80px; +} +.module .screenshot { + width: 600px; + height: 337px; + box-shadow:inset 0 1px 0 rgba(255,255,255,.6), 0 22px 70px 4px rgba(0,0,0,0.56), 0 0 0 1px rgba(0, 0, 0, 0.0); + border-radius: 5px; + background-size: cover; +} + +/* Footer */ +.md-copyright__highlight { + background-image: url('/img/netflix-oss.png'); + background-size: contain; + background-repeat: no-repeat; + color: rgba(0,0,0,0); + height: 60px; +} + +/* Comparison block */ +.compare { + background-color: var(--brand-lt-blue); + padding-top: 80px; + padding-bottom: 80px; + margin: 0px -1000px; + text-align: center; +} +.compare .container { + max-width: 61rem; + margin-left: auto; + margin-right: auto; +} + +.compare .heading { + margin-bottom: 30px; + margin-top: 0px; +} +.compare .bubble { + background: #fff; + border-radius: 10px; + padding: 30px; + height: 100%; +} + +.compare .caption { + font-size: 15px; + line-height: 22px; +} + +.compare .row { + margin: 0 0.8rem; +} \ No newline at end of file diff --git a/docs/devguide/architecture/PollTimeoutSeconds.png b/docs/devguide/architecture/PollTimeoutSeconds.png new file mode 100644 index 0000000000000000000000000000000000000000..35bd3227390e290fb344f16c0682b654a47c0d5d GIT binary patch literal 76891 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∋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 vs global warming graph

+![pirate vs global warming graph](pirate_graph.gif) But in the context of workflows, we're thinking of a graph more like this: -

a regular graph (source: wikipedia)

+![a regular graph (source: wikipedia)](regular_graph.png) Imagine each vertex as a microservice, and the lines are how the microservices are connected together. However, this graph is not a directed graph - as there is no direction given to each connection. @@ -22,7 +22,7 @@ Imagine each vertex as a microservice, and the lines are how the microservices a A directed graph means that there is a direction to each connection. For example, this graph is directed: -

directed graph

+![directed graph](directed_graph.png) Each arrow has a direction, Point "N" can proceed directly to "B", but "B" cannot proceed to "N" in the opposite direction. @@ -34,13 +34,13 @@ So a Directed Acyclic Graph is a set of vertices where the connections are direc Since a Conductor workflow is a series of vertices that can connect in only a specific direction and cannot loop, a Conductor workflow is thus a directed acyclic graph: -

Conductor Dag

+![Conductor Dag](dag_workflow.png) ### Can a workflow have loops and still be a DAG? Yes. For example, Conductor workflows have Do-While loops: -

Conductor Dag

+![Conductor Dag](dag_workflow2.png) This is still a DAG, because the loop is just shorthand for running the tasks inside the loop over and over again. For example, if the 2nd loop in the above image is run 3 times, the workflow path will be: diff --git a/docs/docs/img/directed_graph.png b/docs/devguide/architecture/directed_graph.png similarity index 100% rename from docs/docs/img/directed_graph.png rename to docs/devguide/architecture/directed_graph.png diff --git a/docs/docs/architecture/overview.md b/docs/devguide/architecture/index.md similarity index 88% rename from docs/docs/architecture/overview.md rename to docs/devguide/architecture/index.md index 81bd00e38..986003dfb 100644 --- a/docs/docs/architecture/overview.md +++ b/docs/devguide/architecture/index.md @@ -1,15 +1,15 @@ -# Overview +# Architecture Overview -![Architecture diagram](/img/conductor-architecture.png) +![Architecture diagram](conductor-architecture.png) The API and storage layers are pluggable and provide ability to work with different backends and queue service providers. ## Runtime Model Conductor follows RPC based communication model where workers are running on a separate machine from the server. Workers communicate with server over HTTP based endpoints and employs polling model for managing work queues. -![Runtime Model of Conductor](/img/overview.png) +![Runtime Model of Conductor](overview.png) -**Notes** +## Notes * Workers are remote systems that communicate over HTTP with the conductor servers. * Task Queues are used to schedule tasks for workers. We use [dyno-queues][1] internally but it can easily be swapped with SQS or similar pub-sub mechanism. diff --git a/docs/docs/img/overview.png b/docs/devguide/architecture/overview.png similarity index 100% rename from docs/docs/img/overview.png rename to docs/devguide/architecture/overview.png diff --git a/docs/docs/img/pirate_graph.gif b/docs/devguide/architecture/pirate_graph.gif similarity index 100% rename from docs/docs/img/pirate_graph.gif rename to docs/devguide/architecture/pirate_graph.gif diff --git a/docs/docs/img/regular_graph.png b/docs/devguide/architecture/regular_graph.png similarity index 100% rename from docs/docs/img/regular_graph.png rename to docs/devguide/architecture/regular_graph.png diff --git a/docs/docs/img/task_states.png b/docs/devguide/architecture/task_states.png similarity index 100% rename from docs/docs/img/task_states.png rename to docs/devguide/architecture/task_states.png diff --git a/docs/docs/architecture/tasklifecycle.md b/docs/devguide/architecture/tasklifecycle.md similarity index 54% rename from docs/docs/architecture/tasklifecycle.md rename to docs/devguide/architecture/tasklifecycle.md index 036071df8..09d7f5371 100644 --- a/docs/docs/architecture/tasklifecycle.md +++ b/docs/devguide/architecture/tasklifecycle.md @@ -1,47 +1,59 @@ +# Task Lifecycle + ## Task state transitions + The figure below depicts the state transitions that a task can go through within a workflow execution. -![Task_States](/img/task_states.png) +![Task States](task_states.png) ## Retries and Failure Scenarios ### Task failure and retries -Retries for failed task executions of each task can be configured independently. retryCount, retryDelaySeconds and retryLogic can be used to configure the retry mechanism. -![Task Failure](/img/TaskFailure.png) +Retries for failed task executions of each task can be configured independently. `retryCount`, `retryDelaySeconds` and `retryLogic` can be used to configure the retry mechanism. + +![Task Failure](TaskFailure.png) 1. Worker (W1) polls for task T1 from the Conductor server and receives the task. 2. Upon processing this task, the worker determines that the task execution is a failure and reports this to the server with FAILED status after 10 seconds. 3. The server will persist this FAILED execution of T1. A new execution of task T1 will be created and scheduled to be polled. This task will be available to be polled after 5 (retryDelaySeconds) seconds. +### Poll Timeout Seconds + +Poll timeout is the maximum amount of time by which a worker needs to poll a task, else the task will be marked as `TIMED_OUT`. + +![Task Poll Timeout](PollTimeoutSeconds.png) + +In the figure above, task T1 does not get polled by the worker within 60 seconds, so Conductor marks it as `TIMED_OUT`. ### Timeout seconds -Timeout is the maximum amount of time that the task must reach a terminal state in, else the task will be marked as TIMED_OUT. -![Task Timeout](/img/TimeoutSeconds.png) +Timeout is the maximum amount of time that the task must reach a terminal state in, else it will be marked as `TIMED_OUT`. + +![Task Timeout](TimeoutSeconds.png) -**0 seconds** -> Worker polls for task T1 from the Conductor server and receives the task. T1 is put into IN_PROGRESS status by the server. -Worker starts processing the task but is unable to process the task at this time. Worker updates the server with T1 set to IN_PROGRESS status and a callback of 9 seconds. +**0 seconds** -> Worker polls for task T1 from the Conductor server and receives the task. T1 is put into `IN_PROGRESS` status by the server. +Worker starts processing the task but is unable to process the task at this time. Worker updates the server with T1 set to `IN_PROGRESS` status and a callback of 9 seconds. Server puts T1 back in the queue but makes it invisible and the worker continues to poll for the task but does not receive T1 for 9 seconds. **9,18 seconds** -> Worker receives T1 from the server and is still unable to process the task and updates the server with a callback of 9 seconds. **27 seconds** -> Worker polls and receives task T1 from the server and is now able to process this task. -**30 seconds** (T1 timeout) -> Server marks T1 as TIMED_OUT because it is not in a terminal state after first being moved to IN_PROGRESS status. Server schedules a new task based on the retry count. - -**32 seconds** -> Worker completes processing of T1 and updates the server with COMPLETED status. Server will ignore this update since T1 has already been moved to a terminal status (TIMED_OUT). +**30 seconds** (T1 timeout) -> Server marks T1 as `TIMED_OUT` because it is not in a terminal state after first being moved to `IN_PROGRESS` status. Server schedules a new task based on the retry count. +**32 seconds** -> Worker completes processing of T1 and updates the server with `COMPLETED` status. Server will ignore this update since T1 has already been moved to a terminal status (`TIMED_OUT`). ### Response timeout seconds + Response timeout is the time within which the worker must respond to the server with an update for the task, else the task will be marked as TIMED_OUT. -![Response Timeout](/img/ResponseTimeoutSeconds.png) +![Response Timeout](ResponseTimeoutSeconds.png) -**0 seconds** -> Worker polls for the task T1 from the Conductor server and receives the task. T1 is put into IN_PROGRESS status by the server. +**0 seconds** -> Worker polls for the task T1 from the Conductor server and receives the task. T1 is put into `IN_PROGRESS` status by the server. Worker starts processing the task but the worker instance dies during this execution. -**20 seconds** (T1 responseTimeout) -> Server marks T1 as TIMED_OUT since the task has not been updated by the worker within the configured responseTimeoutSeconds (20). A new instance of task T1 is scheduled as per the retry configuration. +**20 seconds** (T1 responseTimeout) -> Server marks T1 as `TIMED_OUT` since the task has not been updated by the worker within the configured responseTimeoutSeconds (20). A new instance of task T1 is scheduled as per the retry configuration. **25 seconds** -> The retried instance of T1 is available to be polled by the worker, after the retryDelaySeconds (5) has elapsed. diff --git a/docs/docs/technicaldetails.md b/docs/devguide/architecture/technicaldetails.md similarity index 94% rename from docs/docs/technicaldetails.md rename to docs/devguide/architecture/technicaldetails.md index afcc8f5a0..934489207 100644 --- a/docs/docs/technicaldetails.md +++ b/docs/devguide/architecture/technicaldetails.md @@ -7,13 +7,13 @@ The proto models are auto-generated at compile time using this ProtoGen library. ### Cassandra Persistence -The Cassandra persistence layer currently provides a partial implementation of the ExecutionDAO that supports all the CRUD operations for tasks and workflow execution. The data modelling is done in a denormalized manner and stored in two tables. The “workflows” table houses all the information for a workflow execution including all its tasks and is the source of truth for all the information regarding a workflow and its tasks. The “task_lookup” table, as the name suggests stores a lookup of taskIds to workflowId. This table facilitates the fast retrieval of task data given a taskId. +The Cassandra persistence layer currently provides a partial implementation of the ExecutionDAO that supports all the CRUD operations for tasks and workflow execution. The data modelling is done in a denormalized manner and stored in two tables. The "workflows" table houses all the information for a workflow execution including all its tasks and is the source of truth for all the information regarding a workflow and its tasks. The "task_lookup" table, as the name suggests stores a lookup of taskIds to workflowId. This table facilitates the fast retrieval of task data given a taskId. All the datastore operations that are used during the critical execution path of a workflow have been implemented currently. Few of the operational abilities of the ExecutionDAO are yet to be implemented. This module also does not provide implementations for QueueDAO, PollDataDAO and RateLimitingDAO. We envision using the Cassandra DAO with an external queue implementation, since implementing a queuing recipe on top of Cassandra is an anti-pattern that we want to stay away from. ### External Payload Storage The implementation of this feature is such that the externalization of payloads is fully transparent and automated to the user. Conductor operators can configure the usage of this feature and is completely abstracted and hidden from the user, thereby allowing the operators full control over the barrier limits. Currently, only AWS S3 is supported as a storage system, however, as with all other Conductor components, this is pluggable and can be extended to enable any other object store to be used as an external payload storage system. -The externalization of payloads is enforced using two kinds of [barriers](/externalpayloadstorage.html). Soft barriers are used when the payload size is warranted enough to be stored as part of workflow execution. These payloads will be stored in external storage and used during execution. Hard barriers are enforced to safeguard against voluminous data, and such payloads are rejected and the workflow execution is failed. +The externalization of payloads is enforced using two kinds of [barriers](../../documentation/advanced/externalpayloadstorage.md). Soft barriers are used when the payload size is warranted enough to be stored as part of workflow execution. These payloads will be stored in external storage and used during execution. Hard barriers are enforced to safeguard against voluminous data, and such payloads are rejected and the workflow execution is failed. The payload size is evaluated in the client before being sent over the wire to the server. If the payload size exceeds the configured soft limit, the client makes a request to the server for the location at which the payload is to be stored. In this case where S3 is being used, the server returns a signed url for the location and the client uploads the payload using this signed url. The relative path to the payload object is then stored in the workflow/task metadata. The server can then download this payload from this path and use as needed during execution. This allows the server to control access to the S3 bucket, thereby making the user applications where the worker processes are run completely agnostic of the permissions needed to access this location. diff --git a/docs/docs/bestpractices.md b/docs/devguide/bestpractices.md similarity index 97% rename from docs/docs/bestpractices.md rename to docs/devguide/bestpractices.md index 3889bddaf..d753cbbc8 100644 --- a/docs/docs/bestpractices.md +++ b/docs/devguide/bestpractices.md @@ -1,3 +1,5 @@ +# Best Practices + ## Response Timeout - Configure the responseTimeoutSeconds of each task to be > 0. - Should be less than or equal to timeoutSeconds. diff --git a/docs/devguide/concepts/index.md b/docs/devguide/concepts/index.md new file mode 100644 index 000000000..6eace3d85 --- /dev/null +++ b/docs/devguide/concepts/index.md @@ -0,0 +1,17 @@ +# Basic Concepts +Conductor allows you to build a complex application using simple and granular tasks that do not +need to be aware of or keep track of the state of your application's execution flow. Conductor keeps track of the state, +calls tasks in the right order (sequentially or in parallel, as defined by you), retry calls if needed, handle failure +scenarios gracefully, and outputs the final result. + + +![Workflow screnshot](../../home/devex.png) + +Leveraging workflows in Conductor enables developers to truly focus on their core mission - building their application +code in the languages of their choice. Conductor does the heavy lifting associated with ensuring high +reliability, transactional consistency, and long durability of their workflows. Simply put, wherever your application's +component lives and whichever languages they were written in, you can build a workflow in Conductor to orchestrate their +execution in a reliable & scalable manner. + +[Workflows](workflows.md) and [Tasks](tasks.md) are the two key concepts that underlie the Conductor system. + diff --git a/docs/devguide/concepts/tasks.md b/docs/devguide/concepts/tasks.md new file mode 100644 index 000000000..062ad20cd --- /dev/null +++ b/docs/devguide/concepts/tasks.md @@ -0,0 +1,32 @@ +# Tasks +Tasks are the building blocks of Conductor Workflows. There must be at least one task configured in each Workflow Definition. A typical Conductor workflow defines a lists of tasks that are executed until the completion or termination of the workflow. + +Tasks can be categorized into three types: + +## Types of Tasks +### System Tasks +[**System Tasks**](../../documentation/configuration/workflowdef/systemtasks/index.md) are built-in tasks that are general purpose and re-usable. They are executed within the JVM of the Conductor server and managed by Conductor for execution and scalability. Such tasks allow you to get started without having to write custom workers. + +### Simple Tasks +[**Simple Tasks**](workers.md) or Worker Tasks are implemented by your application and run in a separate environment from Conductor. These tasks talk to the Conductor server via REST/gRPC to poll for tasks and update its status after execution. + +### Operators +[**Operators**](../../documentation/configuration/workflowdef/operators/index.md) are built-in primitives in Conductor that allow you control the flow of tasks in your workflow. Operators are similar to programming constructs such as `for` loops, `switch` blocks, etc. + +## Task Configuration +Task Configurations appear within the `tasks` array property of the Workflow Definition. This array is the blueprint that describes how a workflow will process an input payload by passing it through successive tasks. + +* For all tasks, the configuration will specifiy what **input parameters** the task takes. +* For SIMPLE (worker based) tasks, the configuration will contain a reference to a registered worker `taskName`. +* For System Tasks and Operators, the task configuration will contain important parameters that control the behavior of the task. For example, the task configuration of an HTTP task will specify an endpoint URL and the templatized payload that it will be called with when the task executes. + +## Task Definition +Not to be confused with Task Configurations, [Task Definitions](../../documentation/configuration/taskdef.md) help define default task level parameters like inputs and outputs, timeouts, retries etc. for SIMPLE (i.e. worker implemented) tasks. + +* All simple tasks need to be registered before they can be used by active workflows. +* Task definitions can be registered via the UI, or through the API. +* A registered task definition can be referenced from within different workflows. + +## Task Execution +Each time a workload is passed into a configured task, a Task Execution object is created. This object has a unique ID and represents the result of the operation. This includes the status (i.e. whether the task was completed successfully), and any input, output and variables associated with the task. + diff --git a/docs/docs/gettingstarted/intro.md b/docs/devguide/concepts/why.md similarity index 86% rename from docs/docs/gettingstarted/intro.md rename to docs/devguide/concepts/why.md index 789a7db4b..8acb0d15b 100644 --- a/docs/docs/gettingstarted/intro.md +++ b/docs/devguide/concepts/why.md @@ -1,5 +1,7 @@ # Why Conductor? -## Conductor was built to help Netflix orchestrate microservices based process flows with the following features: +Conductor was built to help Netflix orchestrate microservices based process flows. + +## Features * A distributed server ecosystem, which stores workflow state information efficiently. * Allow creation of process / business flows in which each individual task can be implemented by the same / different microservices. @@ -23,6 +25,6 @@ With peer to peer task choreography, we found it was harder to scale with growing business needs and complexities. Pub/sub model worked for simplest of the flows, but quickly highlighted some of the issues associated with the approach: -* Process flows are “embedded” within the code of multiple application. +* Process flows are "embedded" within the code of multiple application. * Often, there is tight coupling and assumptions around input/output, SLAs etc, making it harder to adapt to changing needs. -* Almost no way to systematically answer “How much are we done with process X”? +* Almost no way to systematically answer "How much are we done with process X"? diff --git a/docs/devguide/concepts/workers.md b/docs/devguide/concepts/workers.md new file mode 100644 index 000000000..faf6c6c85 --- /dev/null +++ b/docs/devguide/concepts/workers.md @@ -0,0 +1,11 @@ +# Workers +A worker is responsible for executing a task. Workers can be implemented in any language, and Conductor provides a polyglot set of worker frameworks that provide features such as polling threads, metrics and server communication that makes creating workers easy. + +Each worker embodies the Microservice design pattern and follows certain basic principles: + +1. Workers are stateless and do not implement a workflow specific logic. +2. Each worker executes a very specific task and produces well defined output given specific inputs. +3. Workers are meant to be idempotent (or should handle cases where the task that partially executed gets rescheduled due to timeouts etc.) +4. Workers do not implement the logic to handle retries etc, that is taken care by the Conductor server. + +Conductor maintains a registry of worker tasks. A task MUST be registered before being used in a workflow. This can be done by creating and saving a **Task Definition**. \ No newline at end of file diff --git a/docs/devguide/concepts/workflows.md b/docs/devguide/concepts/workflows.md new file mode 100644 index 000000000..d4c17618c --- /dev/null +++ b/docs/devguide/concepts/workflows.md @@ -0,0 +1,13 @@ +# Workflows +We will talk about two distinct topics, *defining* a workflow and *executing* a workflow. + +### Workflow Definition +The Workflow Definition is the Conductor primitive that encompasses the flow of your business logic. It contains all the information necessary to describe the behavior of a workflow. + +A Workflow Definition contains a collection of **Task Configurations**. This is the blueprint which specifies the order of execution of +tasks within a workflow. This blueprint also specifies how data/state is passed from one task to another (using task input/output parameters). + +Additionally, the Workflow Definition contains metadata regulating the runtime behavior workflow, such what input and output parameters are expected for the entire workflow, and the workflow's the timeout and retry settings. + +### Workflow Execution +If Workflow Definitions are like OOP classes, then Workflows Executions are like object instances. Each time a Workflow Definition is invoked with a given input, a new *Workflow Execution* with a unique ID is created. Definitions to Executions have a 1:N relationship. diff --git a/docs/docs/faq.md b/docs/devguide/faq.md similarity index 68% rename from docs/docs/faq.md rename to docs/devguide/faq.md index 858d0ecfe..5d14a1835 100644 --- a/docs/docs/faq.md +++ b/docs/devguide/faq.md @@ -1,41 +1,41 @@ # Frequently asked Questions -### How do you schedule a task to be put in the queue after some time (e.g. 1 hour, 1 day etc.) +## How do you schedule a task to be put in the queue after some time (e.g. 1 hour, 1 day etc.) After polling for the task update the status of the task to `IN_PROGRESS` and set the `callbackAfterSeconds` value to the desired time. The task will remain in the queue until the specified second before worker polling for it will receive it again. If there is a timeout set for the task, and the `callbackAfterSeconds` exceeds the timeout value, it will result in task being TIMED_OUT. -### How long can a workflow be in running state? Can I have a workflow that keeps running for days or months? +## How long can a workflow be in running state? Can I have a workflow that keeps running for days or months? Yes. As long as the timeouts on the tasks are set to handle long running workflows, it will stay in running state. -### My workflow fails to start with missing task error +## My workflow fails to start with missing task error Ensure all the tasks are registered via `/metadata/taskdefs` APIs. Add any missing task definition (as reported in the error) and try again. -### Where does my worker run? How does conductor run my tasks? +## Where does my worker run? How does conductor run my tasks? -Conductor does not run the workers. When a task is scheduled, it is put into the queue maintained by Conductor. Workers are required to poll for tasks using `/tasks/poll` API at periodic interval, execute the business logic for the task and report back the results using `POST /tasks` API call. -Conductor, however will run [system tasks](/configuration/systask.html) on the Conductor server. +Conductor does not run the workers. When a task is scheduled, it is put into the queue maintained by Conductor. Workers are required to poll for tasks using `/tasks/poll` API at periodic interval, execute the business logic for the task and report back the results using `POST {{ api_prefix }}/tasks` API call. +Conductor, however will run [system tasks](../documentation/configuration/workflowdef/systemtasks/index.md) on the Conductor server. -### How can I schedule workflows to run at a specific time? +## How can I schedule workflows to run at a specific time? Netflix Conductor itself does not provide any scheduling mechanism. But there is a community project [_Schedule Conductor Workflows_](https://github.com/jas34/scheduledwf) which provides workflow scheduling capability as a pluggable module as well as workflow server. Other way is you can use any of the available scheduling systems to make REST calls to Conductor to start a workflow. Alternatively, publish a message to a supported eventing system like SQS to trigger a workflow. -More details about [eventing](/configuration/eventhandlers.html). +More details about [eventing](../documentation/configuration/eventhandlers.md). -### How do I setup Dynomite cluster? +## How do I setup Dynomite cluster? Visit Dynomite's [Github page](https://github.com/Netflix/dynomite) to find details on setup and support mechanism. -### Can I use conductor with Ruby / Go / Python? +## Can I use conductor with Ruby / Go / Python? Yes. Workers can be written any language as long as they can poll and update the task results via HTTP endpoints. @@ -44,18 +44,18 @@ Conductor provides frameworks for Java and Python to simplify the task of pollin **Note:** Python and Go clients have been contributed by the community. -### How can I get help with Dynomite? +## How can I get help with Dynomite? Visit Dynomite's [Github page](https://github.com/Netflix/dynomite) to find details on setup and support mechanism. -### My workflow is running and the task is SCHEDULED but it is not being processed. +## My workflow is running and the task is SCHEDULED but it is not being processed. Make sure that the worker is actively polling for this task. Navigate to the `Task Queues` tab on the Conductor UI and select your task name in the search box. Ensure that `Last Poll Time` for this task is current. In Conductor 3.x, ```conductor.redis.availabilityZone``` defaults to ```us-east-1c```. Ensure that this matches where your workers are, and that it also matches```conductor.redis.hosts```. -### How do I configure a notification when my workflow completes or fails? +## How do I configure a notification when my workflow completes or fails? When a workflow fails, you can configure a "failure workflow" to run using the```failureWorkflow``` parameter. By default, three parameters are passed: @@ -65,20 +65,20 @@ When a workflow fails, you can configure a "failure workflow" to run using the`` You can also use the Workflow Status Listener: -* Set the workflowStatusListenerEnabled field in your workflow definition to true which enables [notifications](/configuration/workflowdef.html#workflow-notifications). -* Add a custom implementation of the Workflow Status Listener. Refer [this](/extend.html#workflow-status-listener). -* This notification can be implemented in such a way as to either send a notification to an external system or to send an event on the conductor queue to complete/fail another task in another workflow as described [here](/configuration/eventhandlers.html). +* Set the workflowStatusListenerEnabled field in your workflow definition to true which enables [notifications](../documentation/configuration/workflowdef/index.md#workflow-notifications). +* Add a custom implementation of the Workflow Status Listener. Refer [this](../documentation/advanced/extend.md#workflow-status-listener). +* This notification can be implemented in such a way as to either send a notification to an external system or to send an event on the conductor queue to complete/fail another task in another workflow as described [here](../documentation/configuration/eventhandlers.md). -Refer to this [documentation](/configuration/workflowdef.html#workflow-notifications) to extend conductor to send out events/notifications upon workflow completion/failure. +Refer to this [documentation](../documentation/configuration/workflowdef/index.md#workflow-notifications) to extend conductor to send out events/notifications upon workflow completion/failure. -### I want my worker to stop polling and executing tasks when the process is being terminated. (Java client) +## I want my worker to stop polling and executing tasks when the process is being terminated. (Java client) In a `PreDestroy` block within your application, call the `shutdown()` method on the `TaskRunnerConfigurer` instance that you have created to facilitate a graceful shutdown of your worker in case the process is being terminated. -### Can I exit early from a task without executing the configured automatic retries in the task definition? +## Can I exit early from a task without executing the configured automatic retries in the task definition? Set the status to `FAILED_WITH_TERMINAL_ERROR` in the TaskResult object within your worker. This would mark the task as FAILED and fail the workflow without retrying the task as a fail-fast mechanism. diff --git a/docs/docs/how-tos/Monitoring/Conductor-LogLevel.md b/docs/devguide/how-tos/Monitoring/Conductor-LogLevel.md similarity index 100% rename from docs/docs/how-tos/Monitoring/Conductor-LogLevel.md rename to docs/devguide/how-tos/Monitoring/Conductor-LogLevel.md diff --git a/docs/docs/how-tos/Tasks/creating-tasks.md b/docs/devguide/how-tos/Tasks/creating-tasks.md similarity index 84% rename from docs/docs/how-tos/Tasks/creating-tasks.md rename to docs/devguide/how-tos/Tasks/creating-tasks.md index 54ae74e91..d79da9c96 100644 --- a/docs/docs/how-tos/Tasks/creating-tasks.md +++ b/docs/devguide/how-tos/Tasks/creating-tasks.md @@ -1,24 +1,22 @@ # Creating Task Definitions - Tasks can be created using the tasks metadata API -`POST /api/metadata/taskdefs` +`POST {{ api_prefix }}/metadata/taskdefs` This API takes an array of new task definitions. +## Examples ### Example using curl - ```shell -curl 'http://localhost:8080/api/metadata/taskdefs' \ +curl '{{ server_host }}{{ api_prefix }}/metadata/taskdefs' \ -H 'accept: */*' \ -H 'content-type: application/json' \ --data-raw '[{"createdBy":"user","name":"sample_task_name_1","description":"This is a sample task for demo","responseTimeoutSeconds":10,"timeoutSeconds":30,"inputKeys":[],"outputKeys":[],"timeoutPolicy":"TIME_OUT_WF","retryCount":3,"retryLogic":"FIXED","retryDelaySeconds":5,"inputTemplate":{},"rateLimitPerFrequency":0,"rateLimitFrequencyInSeconds":1}]' ``` ### Example using node fetch - ```javascript -fetch("http://localhost:8080/api/metadata/taskdefs", { +fetch("{{ server_host }}{{ api_prefix }}/metadata/taskdefs", { "headers": { "accept": "*/*", "content-type": "application/json", @@ -28,8 +26,7 @@ fetch("http://localhost:8080/api/metadata/taskdefs", { }); ``` ## Best Practices - 1. You can update a set of tasks together in this API -2. Task configurations are important attributes that controls the behavior of this task in a Workflow. Refer to [Task Configurations](/configuration/taskdef.html) for all the options and details' +2. Task configurations are important attributes that controls the behavior of this task in a Workflow. Refer to [Task Configurations](../../../documentation/configuration/taskdef.md) for all the options and details' 3. You can also use the Conductor Swagger UI to update the tasks diff --git a/docs/devguide/how-tos/Tasks/dynamic-vs-switch-tasks.md b/docs/devguide/how-tos/Tasks/dynamic-vs-switch-tasks.md new file mode 100644 index 000000000..0339f4cc0 --- /dev/null +++ b/docs/devguide/how-tos/Tasks/dynamic-vs-switch-tasks.md @@ -0,0 +1,18 @@ +# Dynamic vs Switch Tasks + +Dynamic Tasks are useful in situations when need to run a task of which the task type is determined at runtime instead +of during the configuration. It is similar to the `SWITCH` use case but with `DYNAMIC` +we won't need to preconfigure all case options in the workflow definition itself. Instead, we can mark the task +as `DYNAMIC` and determine which underlying task does it run during the workflow execution itself. + +* Use DYNAMIC task as a replacement for SWITCH if you have too many case options +* DYNAMIC task is an option when you want to programmatically determine the next task to run instead of using expressions +* DYNAMIC task simplifies the workflow execution UI view which will now only show the selected task +* SWITCH task visualization is helpful as a documentation - showing you all options that the workflow could have + taken +* SWITCH task comes with a default task option which can be useful in some use cases + +Learn more about + +* [Dynamic Tasks](../../../documentation/configuration/workflowdef/operators/dynamic-task.md) +* [Switch Tasks](../../../documentation/configuration/workflowdef/operators/switch-task.md) \ No newline at end of file diff --git a/docs/docs/how-tos/Tasks/extending-system-tasks.md b/docs/devguide/how-tos/Tasks/extending-system-tasks.md similarity index 85% rename from docs/docs/how-tos/Tasks/extending-system-tasks.md rename to docs/devguide/how-tos/Tasks/extending-system-tasks.md index 5661d040c..e219fae61 100644 --- a/docs/docs/how-tos/Tasks/extending-system-tasks.md +++ b/docs/devguide/how-tos/Tasks/extending-system-tasks.md @@ -1,6 +1,6 @@ # Extending System Tasks -[System tasks](/configuration/systask.html) allow Conductor to run simple tasks on the server - removing the need to build (and deploy) workers for basic tasks. This allows for automating more mundane tasks without building specific microservices for them. +[System tasks](../../../documentation/configuration/workflowdef/systemtasks/index.md) allow Conductor to run simple tasks on the server - removing the need to build (and deploy) workers for basic tasks. This allows for automating more mundane tasks without building specific microservices for them. However, sometimes it might be necessary to add additional parameters to a System Task to gain the behavior that is desired. @@ -58,7 +58,7 @@ When this workflow is run - it fails, as expected. Now, sometimes an API call might fail due to an issue on the remote server, and retrying the call will result in a response. With many Conductor tasks, ```retryCount```, ```retryDelaySeconds``` and ```retryLogic``` fields can be applied to retry the worker (with the desired parameters). -By default, the [HTTP Task](/reference-docs/http-task.html) does not have ```retryCount```, ```retryDelaySeconds``` or ```retryLogic``` built in. Attempting to add these parameters to a HTTP Task results in an error. +By default, the [HTTP Task](../../../documentation/configuration/workflowdef/systemtasks/http-task.md) does not have ```retryCount```, ```retryDelaySeconds``` or ```retryLogic``` built in. Attempting to add these parameters to a HTTP Task results in an error. ## The Solution diff --git a/docs/docs/how-tos/Tasks/monitoring-task-queues.md b/docs/devguide/how-tos/Tasks/monitoring-task-queues.md similarity index 72% rename from docs/docs/how-tos/Tasks/monitoring-task-queues.md rename to docs/devguide/how-tos/Tasks/monitoring-task-queues.md index 584b89102..4de10727a 100644 --- a/docs/docs/how-tos/Tasks/monitoring-task-queues.md +++ b/docs/devguide/how-tos/Tasks/monitoring-task-queues.md @@ -1,15 +1,11 @@ ---- -sidebar_position: 1 ---- - # Monitoring Task Queues Conductor offers an API and UI interface to monitor the task queues. This is useful to see details of the number of workers polling and monitoring the queue backlog. -### Using the UI +## Using the UI -```http request +``` /taskQueue ``` @@ -20,20 +16,21 @@ On this screen you can select and view the details of the task queue. The follow 1. Queue Size - The number of tasks waiting to be executed 2. Workers - The count and list of works and their instance reference who are polling for work for this task -### Using APIs +## Using APIs To see the size of the task queue via API: ```shell -curl 'http://localhost:8080/api/tasks/queue/sizes?taskType=' \ +curl '{{ server_host }}{{ api_prefix }}/tasks/queue/sizes?taskType=' \ -H 'accept: */*' ``` To see the worker poll information of the task queue via API: ```shell -curl 'http://localhost:8080/api/tasks/queue/polldata?taskType=' \ +curl '{{ server_host }}{{ api_prefix }}/tasks/queue/polldata?taskType=' \ -H 'accept: */*' ``` -> Replace `` with your task name +!!! note + Replace `` with your task name diff --git a/docs/docs/how-tos/Tasks/reusing-tasks.md b/docs/devguide/how-tos/Tasks/reusing-tasks.md similarity index 96% rename from docs/docs/how-tos/Tasks/reusing-tasks.md rename to docs/devguide/how-tos/Tasks/reusing-tasks.md index 7923a2e43..1b3f2dbd7 100644 --- a/docs/docs/how-tos/Tasks/reusing-tasks.md +++ b/docs/devguide/how-tos/Tasks/reusing-tasks.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Reusing Tasks A powerful feature of Conductor is that it supports and enables re-usability out of the box. Task workers typically diff --git a/docs/docs/how-tos/Tasks/task-configurations.md b/docs/devguide/how-tos/Tasks/task-configurations.md similarity index 54% rename from docs/docs/how-tos/Tasks/task-configurations.md rename to docs/devguide/how-tos/Tasks/task-configurations.md index 1fef6c709..7c25282bc 100644 --- a/docs/docs/how-tos/Tasks/task-configurations.md +++ b/docs/devguide/how-tos/Tasks/task-configurations.md @@ -1,12 +1,8 @@ ---- -sidebar_position: 1 ---- - # Task Configurations -Refer to [Task Definitions](/configuration/taskdef.html) for details on how to configure task definitions +Refer to [Task Definitions](../../../documentation/configuration/taskdef.md) for details on how to configure task definitions -### Example +## Example Here is a task template payload with commonly used fields: @@ -29,7 +25,7 @@ Here is a task template payload with commonly used fields: } ``` -### Best Practices +## Best Practices -1. Refer to [Task Timeouts](/how-tos/Tasks/task-timeouts.html) for additional information on how the various timeout settings work -2. Refer to [Monitoring Task Queues](/how-tos/Tasks/monitoring-task-queues.html) on how to monitor task queues +1. Refer to [Task Timeouts](task-timeouts.md) for additional information on how the various timeout settings work +2. Refer to [Monitoring Task Queues](monitoring-task-queues.md) on how to monitor task queues diff --git a/docs/docs/how-tos/Tasks/task-inputs.md b/docs/devguide/how-tos/Tasks/task-inputs.md similarity index 93% rename from docs/docs/how-tos/Tasks/task-inputs.md rename to docs/devguide/how-tos/Tasks/task-inputs.md index 41f6df61a..53cae8d79 100644 --- a/docs/docs/how-tos/Tasks/task-inputs.md +++ b/docs/devguide/how-tos/Tasks/task-inputs.md @@ -1,13 +1,9 @@ ---- -sidebar_position: 1 ---- - # Task Inputs Task inputs can be provided in multiple ways. This is configured in the workflow definition when a task is participating in the workflow. -### Inputs referred from Workflow inputs +## Inputs referred from Workflow inputs When we start a workflow, we can provide inputs to the workflow in a json format. For example: @@ -38,7 +34,7 @@ In this example, the tasks will receive the following inputs after they are eval } ``` -### Inputs referred from other Task outputs +## Inputs referred from other Task outputs Similar to how we can refer to workflow inputs, we can also refer to an output field that was generated by a task that executed before. @@ -66,7 +62,7 @@ We can refer to these as the new task's input by using the following expression: The expression format is based on [Json Path](https://goessner.net/articles/JsonPath/) and you can construct complex input params based on the syntax. -### Hard coded inputs +## Hard coded inputs Task inputs can also be hard coded in the workflow definitions. This is useful when you have a re-usable task which has configurable options that can be applied in different workflow contexts. diff --git a/docs/docs/how-tos/Tasks/task-timeouts.md b/docs/devguide/how-tos/Tasks/task-timeouts.md similarity index 95% rename from docs/docs/how-tos/Tasks/task-timeouts.md rename to docs/devguide/how-tos/Tasks/task-timeouts.md index 74aa51ab6..eda32e377 100644 --- a/docs/docs/how-tos/Tasks/task-timeouts.md +++ b/docs/devguide/how-tos/Tasks/task-timeouts.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Task Timeouts Tasks can be configured to handle various scenarios of timeouts. Here are some scenarios and the relevance configuration @@ -17,7 +13,7 @@ fields. > `timeoutSeconds` should always be greater than `responseTimeoutSeconds` -### Timeout Seconds +## Timeout Seconds ```json "timeoutSeconds" : 30 @@ -27,7 +23,7 @@ When configured with a value > `0`, the system will wait for this task to comple seconds from when the task is first polled. We can use this to fail a workflow when a task breaches the overall SLA for completion. -### Response Timeout Seconds +## Response Timeout Seconds ```json "responseTimeoutSeconds" : 10 @@ -37,7 +33,7 @@ When configured with a value > `0`, the system will wait for this number of seco the worker updates back with a status. The worker can keep the task in `IN_PROGRESS` state if it requires more time to complete. -### Poll Timeout Seconds +## Poll Timeout Seconds ```json "pollTimeoutSeconds" : 10 diff --git a/docs/docs/how-tos/Tasks/updating-tasks.md b/docs/devguide/how-tos/Tasks/updating-tasks.md similarity index 85% rename from docs/docs/how-tos/Tasks/updating-tasks.md rename to docs/devguide/how-tos/Tasks/updating-tasks.md index 4978e80b3..f38d113f3 100644 --- a/docs/docs/how-tos/Tasks/updating-tasks.md +++ b/docs/devguide/how-tos/Tasks/updating-tasks.md @@ -1,23 +1,19 @@ ---- -sidebar_position: 1 ---- - # Updating Task Definitions Updates to the task definitions can be made using the following API ```http -PUT /api/metadata/taskdefs +PUT {{ api_prefix }}/metadata/taskdefs ``` This API takes a single task definition and updates itself. - +## Examples ### Example using curl ```shell -curl 'http://localhost:8080/api/metadata/taskdefs' \ +curl '{{ server_host }}{{ api_prefix }}/metadata/taskdefs' \ -X 'PUT' \ -H 'accept: */*' \ -H 'content-type: application/json' \ @@ -27,7 +23,7 @@ curl 'http://localhost:8080/api/metadata/taskdefs' \ ### Example using node fetch ```javascript -fetch("http://localhost:8080/api/metadata/taskdefs", { +fetch("{{ server_host }}{{ api_prefix }}/metadata/taskdefs", { "headers": { "accept": "*/*", "content-type": "application/json", @@ -39,4 +35,4 @@ fetch("http://localhost:8080/api/metadata/taskdefs", { ## Best Practices 1. You can also use the Conductor Swagger UI to update the tasks -2. Task configurations are important attributes that controls the behavior of this task in a Workflow. Refer to [Task Configurations](/how-tos/Tasks/task-configurations.html) for all the options and details' +2. Task configurations are important attributes that controls the behavior of this task in a Workflow. Refer to [Task Configurations](task-configurations.md) for all the options and details' diff --git a/docs/docs/how-tos/Workers/build-a-golang-task-worker.md b/docs/devguide/how-tos/Workers/build-a-golang-task-worker.md similarity index 86% rename from docs/docs/how-tos/Workers/build-a-golang-task-worker.md rename to docs/devguide/how-tos/Workers/build-a-golang-task-worker.md index 5581508ba..20cb668d1 100644 --- a/docs/docs/how-tos/Workers/build-a-golang-task-worker.md +++ b/docs/devguide/how-tos/Workers/build-a-golang-task-worker.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Build a Go Task Worker ## Install @@ -11,7 +7,7 @@ go get github.com/netflix/conductor/client/go This will create a Go project under $GOPATH/src and download any dependencies. ## Implementing a Task a Worker -`task`package provides the types used to implement the worker. Here is a reference worker implementation: +`task`package provies the types used to implement the worker. Here is a reference worker implementation: ```go package task @@ -48,7 +44,7 @@ import ( ) func main() { - c := conductor.NewConductorWorker("http://localhost:8080", 1, 10000) + c := conductor.NewConductorWorker("{{ server_host }}", 1, 10000) c.Start("task_1", "", sample.Task_1_Execution_Function, false) c.Start("task_2", "mydomain", sample.Task_2_Execution_Function, true) diff --git a/docs/docs/how-tos/Workers/build-a-java-task-worker.md b/docs/devguide/how-tos/Workers/build-a-java-task-worker.md similarity index 97% rename from docs/docs/how-tos/Workers/build-a-java-task-worker.md rename to docs/devguide/how-tos/Workers/build-a-java-task-worker.md index cf90f656b..322834152 100644 --- a/docs/docs/how-tos/Workers/build-a-java-task-worker.md +++ b/docs/devguide/how-tos/Workers/build-a-java-task-worker.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Build a Java Task Worker This guide provides introduction to building Task Workers in Java. @@ -85,7 +81,7 @@ Use the [Builder](https://github.com/Netflix/conductor/blob/main/client/src/main ```java TaskClient taskClient = new TaskClient(); - taskClient.setRootURI("http://localhost:8080/api/"); //Point this to the server API + taskClient.setRootURI("{{ server_host }}{{ api_prefix }}/"); //Point this to the server API int threadCount = 2; //number of threads used to execute workers. To avoid starvation, should be same or more than number of workers diff --git a/docs/docs/how-tos/Workers/build-a-python-task-worker.md b/docs/devguide/how-tos/Workers/build-a-python-task-worker.md similarity index 95% rename from docs/docs/how-tos/Workers/build-a-python-task-worker.md rename to docs/devguide/how-tos/Workers/build-a-python-task-worker.md index d45657fd1..875571df7 100644 --- a/docs/docs/how-tos/Workers/build-a-python-task-worker.md +++ b/docs/devguide/how-tos/Workers/build-a-python-task-worker.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Build a Python Task Worker ## Install the python client ```shell @@ -28,7 +24,7 @@ def book_car_task(task): def main(): print('Starting Travel Booking workflows') - cc = ConductorWorker('http://localhost:8080/api', 1, 0.1) + cc = ConductorWorker('{{ server_host }}{{ api_prefix }}', 1, 0.1) cc.start('book_flight', book_flight_task, False) cc.start('book_car', book_car_task, True) @@ -39,7 +35,7 @@ if __name__ == '__main__': ```python server_url: str The url to the server hosting the conductor api. - Ex: 'http://localhost:8080/api' + Ex: '{{ server_host }}{{ api_prefix }}' thread_count: int The number of threads that will be polling for and diff --git a/docs/docs/how-tos/Workflows/debugging-workflows.md b/docs/devguide/how-tos/Workflows/debugging-workflows.md similarity index 92% rename from docs/docs/how-tos/Workflows/debugging-workflows.md rename to docs/devguide/how-tos/Workflows/debugging-workflows.md index d15ec07f1..fbcb6f11c 100644 --- a/docs/docs/how-tos/Workflows/debugging-workflows.md +++ b/docs/devguide/how-tos/Workflows/debugging-workflows.md @@ -1,14 +1,10 @@ ---- -sidebar_position: 1 ---- - # Debugging Workflows Conductor UI is a tool that we can leverage for debugging issues. Refer to the following articles to search and view your workflow execution. -1. [Searching Workflows](/how-tos/Workflows/searching-workflows.html) -2. [View Workflow Executions](/how-tos/Workflows/view-workflow-executions.html) +1. [Searching Workflows](searching-workflows.md) +2. [View Workflow Executions](view-workflow-executions.md) ## Debugging Executions @@ -33,7 +29,7 @@ Note: We can also access the task list from **Tasks > Task List** tab. Here is a screen grab of the fields referred above. -![Debugging Wowkflow Execution](/img/tutorial/workflow_debugging.png) +![Debugging Wowkflow Execution](workflow_debugging.png) ## Recovering From Failures diff --git a/docs/docs/how-tos/Workflows/handling-errors.md b/docs/devguide/how-tos/Workflows/handling-errors.md similarity index 97% rename from docs/docs/how-tos/Workflows/handling-errors.md rename to docs/devguide/how-tos/Workflows/handling-errors.md index 1d07e8cdd..b58fe1e91 100644 --- a/docs/docs/how-tos/Workflows/handling-errors.md +++ b/docs/devguide/how-tos/Workflows/handling-errors.md @@ -54,6 +54,6 @@ Here is a sample failure workflow that sends a message to Slack when the workflo } ``` -## Set ```workflowStatusListenerEnabled``` +## Set ```workfowStatusListenerEnabled``` -When this is enabled, notifications are now possible, and by building a custom implementation of the Workflow Status Listener, a notification can be sent to an external service. [More details.](https://github.com/Netflix/conductor/issues/1017#issuecomment-468869173) +When this is enabled, notifications are now possible, and by building a custom implementation of the Workflow Status Listener, a notification can be sent to an external service. [More details.](https://github.com/Netflix/conductor/issues/1017#issuecomment-468869173) \ No newline at end of file diff --git a/docs/docs/how-tos/Workflows/searching-workflows.md b/docs/devguide/how-tos/Workflows/searching-workflows.md similarity index 88% rename from docs/docs/how-tos/Workflows/searching-workflows.md rename to docs/devguide/how-tos/Workflows/searching-workflows.md index be1f82c56..0e18308bc 100644 --- a/docs/docs/how-tos/Workflows/searching-workflows.md +++ b/docs/devguide/how-tos/Workflows/searching-workflows.md @@ -1,19 +1,7 @@ ---- -sidebar_position: 1 ---- - # Searching Workflows In this article we will learn how to search through workflow executions via the UI. -### Prerequisites - -1. Conductor app and UI installed and running in an environment. If required we can look at the following options to get - an environment up and running. - - 1. [Build and Run Conductor Locally](/gettingstarted/local.html) - 2. [Running via Docker Compose](/gettingstarted/docker.html) - ## UI Workflows View Open the home page of the UI installation. It will take you to the `Workflow Executions` view. This is where we can look diff --git a/docs/docs/how-tos/Workflows/starting-workflows.md b/docs/devguide/how-tos/Workflows/starting-workflows.md similarity index 84% rename from docs/docs/how-tos/Workflows/starting-workflows.md rename to docs/devguide/how-tos/Workflows/starting-workflows.md index e1c6ea411..32f586f57 100644 --- a/docs/docs/how-tos/Workflows/starting-workflows.md +++ b/docs/devguide/how-tos/Workflows/starting-workflows.md @@ -3,7 +3,7 @@ Workflow executions can be started by using the following API: ```http -POST /api/workflow/{name} +POST {{ api_prefix }}/workflow/{name} ``` `{name}` is the placeholder for workflow name. The POST API body is your workflow input parameters which can be empty if @@ -17,7 +17,7 @@ Refer to the SDK documentation to configure a client in your selected language t ### Example using curl ```bash -curl 'https://localhost:8080/api/workflow/sample_workflow' \ +curl '{{ server_host }}{{ api_prefix }}/workflow/sample_workflow' \ -H 'accept: text/plain' \ -H 'content-type: application/json' \ --data-raw '{"service":"fedex"}' @@ -29,7 +29,7 @@ is `sample_workflow` ### Example using node fetch ```javascript -fetch("https://localhost:8080/api/workflow/sample_workflow", { +fetch("{{ server_host }}{{ api_prefix }}/workflow/sample_workflow", { "headers": { "accept": "text/plain", "content-type": "application/json", diff --git a/docs/docs/how-tos/Workflows/updating-workflows.md b/docs/devguide/how-tos/Workflows/updating-workflows.md similarity index 84% rename from docs/docs/how-tos/Workflows/updating-workflows.md rename to docs/devguide/how-tos/Workflows/updating-workflows.md index beb67a404..245feeed2 100644 --- a/docs/docs/how-tos/Workflows/updating-workflows.md +++ b/docs/devguide/how-tos/Workflows/updating-workflows.md @@ -3,13 +3,13 @@ Workflows can be created or updated using the workflow metadata API ```html -PUT /api/metadata/workflow +PUT {{ api_prefix }}/metadata/workflow ``` ### Example using curl ```shell -curl 'http://localhost:8080/api/metadata/workflow' \ +curl '{{ server_host }}{{ api_prefix }}/metadata/workflow' \ -X 'PUT' \ -H 'accept: */*' \ -H 'content-type: application/json' \ @@ -19,7 +19,7 @@ curl 'http://localhost:8080/api/metadata/workflow' \ ### Example using node fetch ```javascript -fetch("http://localhost:8080/api/metadata/workflow", { +fetch("{{ server_host }}{{ api_prefix }}/metadata/workflow", { "headers": { "accept": "*/*", "content-type": "application/json" diff --git a/docs/docs/how-tos/Workflows/versioning-workflows.md b/docs/devguide/how-tos/Workflows/versioning-workflows.md similarity index 97% rename from docs/docs/how-tos/Workflows/versioning-workflows.md rename to docs/devguide/how-tos/Workflows/versioning-workflows.md index e44485176..14c9ea93b 100644 --- a/docs/docs/how-tos/Workflows/versioning-workflows.md +++ b/docs/devguide/how-tos/Workflows/versioning-workflows.md @@ -1,7 +1,3 @@ ---- -sidebar_position: 1 ---- - # Versioning Workflows Every workflow has a version number (this number **must** be an integer.) diff --git a/docs/docs/how-tos/Workflows/view-workflow-executions.md b/docs/devguide/how-tos/Workflows/view-workflow-executions.md similarity index 84% rename from docs/docs/how-tos/Workflows/view-workflow-executions.md rename to docs/devguide/how-tos/Workflows/view-workflow-executions.md index 445fc9b29..09f912978 100644 --- a/docs/docs/how-tos/Workflows/view-workflow-executions.md +++ b/docs/devguide/how-tos/Workflows/view-workflow-executions.md @@ -1,22 +1,10 @@ ---- -sidebar_position: 1 ---- - # View Workflow Executions In this article we will learn how to view workflow executions via the UI. -### Prerequisites - -1. Conductor app and UI installed and running in an environment. If required we can look at the following options to get - an environment up and running. - - 1. [Build and Run Conductor Locally](/gettingstarted/local.html) - 2. [Running via Docker Compose](/gettingstarted/docker.html) - ### Viewing a Workflow Execution -Refer to [Searching Workflows](/how-tos/Workflows/searching-workflows.html) to filter and find an execution you want to +Refer to [Searching Workflows](searching-workflows.md) to filter and find an execution you want to view. Click on the workflow id hyperlink to open the Workflow Execution Details page. The following tabs are available to view the details of the Workflow Execution @@ -50,8 +38,8 @@ opens a flyout panel from the side and contains the following tabs. An exciting feature of conductor is the ability to see the exact execution path of a workflow. The executed paths are shown in green and is easy to follow like the example below. The alternative paths are greyed out for reference -![Conductor UI - Workflow Run](/img/tutorial/workflow_execution_view.png) +![Conductor UI - Workflow Run](workflow_execution_view.png) Errors will be visible on the UI in ref such as the example below -![Conductor UI - Failed Task](/img/tutorial/workflow_task_fail.png) +![Conductor UI - Failed Task](workflow_task_fail.png) diff --git a/docs/docs/img/tutorial/workflow_debugging.png b/docs/devguide/how-tos/Workflows/workflow_debugging.png similarity index 100% rename from docs/docs/img/tutorial/workflow_debugging.png rename to docs/devguide/how-tos/Workflows/workflow_debugging.png diff --git a/docs/docs/img/tutorial/workflow_execution_view.png b/docs/devguide/how-tos/Workflows/workflow_execution_view.png similarity index 100% rename from docs/docs/img/tutorial/workflow_execution_view.png rename to docs/devguide/how-tos/Workflows/workflow_execution_view.png diff --git a/docs/docs/img/tutorial/workflow_task_fail.png b/docs/devguide/how-tos/Workflows/workflow_task_fail.png similarity index 100% rename from docs/docs/img/tutorial/workflow_task_fail.png rename to docs/devguide/how-tos/Workflows/workflow_task_fail.png diff --git a/docs/docs/labs/eventhandlers.md b/docs/devguide/labs/eventhandlers.md similarity index 89% rename from docs/docs/labs/eventhandlers.md rename to docs/devguide/labs/eventhandlers.md index 16130e355..3219f3b5e 100644 --- a/docs/docs/labs/eventhandlers.md +++ b/docs/devguide/labs/eventhandlers.md @@ -1,21 +1,16 @@ # Events and Event Handlers -## About -In this Lab, we shall: +In this exercise, we shall: * Publish an Event to Conductor using `Event` task. * Subscribe to Events, and perform actions: * Start a Workflow * Complete Task -Conductor Supports Eventing with two Interfaces: +Conductor supports eventing with two Interfaces: -* [Event Task](/configuration/systask.html#event) -* [Event Handlers](/configuration/eventhandlers.html#event-handler) - -We shall create a simple cyclic workflow similar to this: - -![img](img/EventHandlerCycle.png) +* [Event Task](../../documentation/configuration/workflowdef/systemtasks/event-task.md) +* [Event Handlers](../../documentation/configuration/eventhandlers.md) ## Create Workflow Definitions @@ -43,8 +38,7 @@ Send `POST` requests to `/metadata/workflow` endpoint with below payloads: "taskReferenceName": "test_task_tobe_completed_by_eventHandler", "type": "WAIT" } - ], - "ownerEmail": "example@email.com" + ] } ``` @@ -63,8 +57,7 @@ Send `POST` requests to `/metadata/workflow` endpoint with below payloads: "type": "EVENT", "sink": "conductor" } - ], - "ownerEmail": "example@email.com" + ] } ``` @@ -73,7 +66,7 @@ Send `POST` requests to `/metadata/workflow` endpoint with below payloads: `EVENT` task is a System task, and we shall define it just like other Tasks in Workflow, with `sink` parameter. Also, `EVENT` task doesn't have to be registered before using in Workflow. This is also true for the `WAIT` task. Hence, we will not be registering any tasks for these workflows. -## Events are sent, but they're not handled (yet) +### Events are sent, but they're not handled (yet) Once you try to start `test_workflow_for_eventHandler` workflow, you would notice that the event is sent successfully, but the second worflow `test_workflow_startedBy_eventHandler` is not started. We have sent the Events, but we also need to define `Event Handlers` for Conductor to take any `actions` based on the Event. Let's create `Event Handlers`. @@ -117,7 +110,7 @@ Event Handler can perform a list of actions defined in `actions` array parameter } ``` -Let's define `start_workflow` action. We shall pass the name of workflow we would like to start. The `start_workflow` parameter can use any of the values from the general [Start Workflow Request](/gettingstarted/startworkflow.html). Here we are passing in the workflowId, so that the Complete Task Event Handler can use it. +Let's define `start_workflow` action. We shall pass the name of workflow we would like to start. The `start_workflow` parameter can use any of the values from the general [Start Workflow Request](../../documentation/api/startworkflow.md). Here we are passing in the workflowId, so that the Complete Task Event Handler can use it. ```json { @@ -171,7 +164,7 @@ Similarly, create another Event Handler to complete task. } ``` -## Final flow of Workflow +## Summary After wiring all of the above, starting the `test_workflow_for_eventHandler` should: diff --git a/docs/docs/labs/running-first-workflow.md b/docs/devguide/labs/first-workflow.md similarity index 75% rename from docs/docs/labs/running-first-workflow.md rename to docs/devguide/labs/first-workflow.md index 1abdc3787..a51aa209d 100644 --- a/docs/docs/labs/running-first-workflow.md +++ b/docs/devguide/labs/first-workflow.md @@ -4,16 +4,9 @@ In this article we will explore how we can run a really simple workflow that run Conductor can orchestrate HTTP services out of the box without implementing any code. We will use that to create and run the first workflow. -See [System Task](/configuration/systask.html) for the list of such built-in tasks. +See [System Task](../../documentation/configuration/workflowdef/systemtasks/index.md) for the list of such built-in tasks. Using system tasks is a great way to run a lot of our code in production. -To bring up a local instance of Conductor follow one of the recommended steps: - -1. [Running Locally - From Code](/gettingstarted/local.html) -2. [Running Locally - Docker Compose](/gettingstarted/docker.html) - ---- - ## Configuring our First Workflow This is a sample workflow that we can leverage for our test. @@ -100,30 +93,30 @@ Ok, now that we have walked through our workflow details, let's run this and see To configure the workflow, head over to the swagger API of conductor server and access the metadata workflow create API: -[http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create](http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create) +[http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create) If the link doesn’t open the right Swagger section, we can navigate to Metadata-Resource -→ `POST /api/metadata/workflow` +→ `POST {{ api_prefix }}/metadata/workflow` -![Swagger UI - Metadata - Workflow](/img/tutorial/metadataWorkflowPost.png) +![Swagger UI - Metadata - Workflow](metadataWorkflowPost.png) Paste the workflow payload into the Swagger API and hit Execute. Now if we head over to the UI, we can see this workflow definition created: -![Conductor UI - Workflow Definition](/img/tutorial/uiWorkflowDefinition.png) +![Conductor UI - Workflow Definition](uiWorkflowDefinition.png) If we click through we can see a visual representation of the workflow: -![Conductor UI - Workflow Definition - Visual Flow](/img/tutorial/uiWorkflowDefinitionVisual.png) +![Conductor UI - Workflow Definition - Visual Flow](uiWorkflowDefinitionVisual.png) -## 2. Running our First Workflow +## Running our First Workflow Let’s run this workflow. To do that we can use the swagger API under the workflow-resources -[http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1](http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1) +[http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1) -![Swagger UI - Metadata - Workflow - Run](/img/tutorial/metadataWorkflowRun.png) +![Swagger UI - Metadata - Workflow - Run](metadataWorkflowRun.png) Hit **Execute**! @@ -131,7 +124,7 @@ Conductor will return a workflow id. We will need to use this id to load this up search enabled we wouldn't need to copy this. If we don't have search enabled (using Elasticsearch) copy it from the Swagger UI. -![Swagger UI - Metadata - Workflow - Run](/img/tutorial/workflowRunIdCopy.png) +![Swagger UI - Metadata - Workflow - Run](workflowRunIdCopy.png) Ok, we should see this running and get completed soon. Let’s go to the UI to see what happened. @@ -144,16 +137,12 @@ http://localhost:5000/execution/ Replace `` with our workflow id from the previous step. We should see a screen like below. Click on the different tabs to see all inputs and outputs and task list etc. Explore away! -![Conductor UI - Workflow Run](/img/tutorial/workflowLoaded.png) +![Conductor UI - Workflow Run](workflowLoaded.png) ## Summary -In this blog post — we learned how to run a sample workflow in our Conductor installation. Concepts we touched on: +In this article — we learned how to run a sample workflow in our Conductor installation. Concepts we touched on: 1. Workflow creation 2. System tasks such as HTTP 3. Running a workflow via API - -Thank you for reading, and we hope you found this helpful. Please feel free to reach out to us for any questions and we -are happy to help in any way we can. - diff --git a/docs/docs/labs/img/EventHandlerCycle.png b/docs/devguide/labs/img/EventHandlerCycle.png similarity index 100% rename from docs/docs/labs/img/EventHandlerCycle.png rename to docs/devguide/labs/img/EventHandlerCycle.png diff --git a/docs/docs/labs/img/bgnr_complete_workflow.png b/docs/devguide/labs/img/bgnr_complete_workflow.png similarity index 100% rename from docs/docs/labs/img/bgnr_complete_workflow.png rename to docs/devguide/labs/img/bgnr_complete_workflow.png diff --git a/docs/docs/labs/img/bgnr_state_scheduled.png b/docs/devguide/labs/img/bgnr_state_scheduled.png similarity index 100% rename from docs/docs/labs/img/bgnr_state_scheduled.png rename to docs/devguide/labs/img/bgnr_state_scheduled.png diff --git a/docs/docs/labs/img/bgnr_systask_state.png b/docs/devguide/labs/img/bgnr_systask_state.png similarity index 100% rename from docs/docs/labs/img/bgnr_systask_state.png rename to docs/devguide/labs/img/bgnr_systask_state.png diff --git a/docs/devguide/labs/index.md b/docs/devguide/labs/index.md new file mode 100644 index 000000000..99a15fe28 --- /dev/null +++ b/docs/devguide/labs/index.md @@ -0,0 +1,23 @@ +# Guided Tutorial + +## High Level Steps +Generally, these are the steps necessary in order to put Conductor to work for your business workflow: + +1. Create task worker(s) that poll for scheduled tasks at regular interval +2. Create task definitions for these workers and register them. +3. Create the workflow definition + +## Before We Begin +Ensure you have a Conductor instance up and running. This includes both the Server and the UI. We recommend following the [Docker Instructions](../running/docker.md). + +## Tools +For the purpose of testing and issuing API calls, the following tools are useful + +- Linux cURL command +- [Postman](https://www.postman.com) or similar REST client + +## Let's Go +We will begin by defining a simple workflow that utilizes System Tasks. + +[Next](first-workflow.md) + diff --git a/docs/docs/labs/kitchensink.md b/docs/devguide/labs/kitchensink.md similarity index 91% rename from docs/docs/labs/kitchensink.md rename to docs/devguide/labs/kitchensink.md index d3f7f403f..0173851bd 100644 --- a/docs/docs/labs/kitchensink.md +++ b/docs/devguide/labs/kitchensink.md @@ -163,18 +163,19 @@ An example kitchensink workflow that demonstrates the usage of all the schema co } ``` ### Visual Flow -![img](/img/kitchensink.png) +![img](kitchensink.png) ### Running Kitchensink Workflow -1. Start the server as documented [here](/gettingstarted/docker.html). Use ```-DloadSample=true``` java system property when launching the server. This will create a kitchensink workflow, related task definitions and kick off an instance of kitchensink workflow. -2. Once the workflow has started, the first task remains in the ```SCHEDULED``` state. This is because no workers are currently polling for the task. +1. If you are running Conductor locally, use the `-DloadSample=true` Java system property when launching the server. This will create a kitchensink workflow, +related task definitions and kick off an instance of kitchensink workflow. Otherwise, you can create a new Workflow Definition in the UI by copying the sample above. +2. Once the workflow has started, the first task remains in the `SCHEDULED` state. This is because no workers are currently polling for the task. 3. We will use the REST endpoints directly to poll for tasks and updating the status. #### Start workflow execution Start the execution of the kitchensink workflow by posting the following: ```shell -curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' 'http://localhost:8080/api/workflow/kitchensink' -d ' +curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' '{{ server_host }}{{ api_prefix }}/workflow/kitchensink' -d ' { "task2Name": "task_5" } @@ -185,7 +186,7 @@ The response is a text string identifying the workflow instance id. #### Poll for the first task: ```shell -curl http://localhost:8080/api/tasks/poll/task_1 +curl {{ server_host }}{{ api_prefix }}/tasks/poll/task_1 ``` The response should look something like: @@ -223,7 +224,7 @@ The response should look something like: * Update the status of the task as ```COMPLETED``` as below: ```json -curl -H 'Content-Type:application/json' -H 'Accept:application/json' -X POST http://localhost:8080/api/tasks/ -d ' +curl -H 'Content-Type:application/json' -H 'Accept:application/json' -X POST {{ server_host }}{{ api_prefix }}/tasks/ -d ' { "taskId": "b9eea7dd-3fbd-46b9-a9ff-b00279459476", "workflowInstanceId": "b0d1a935-3d74-46fd-92b2-0ca1e388659f", diff --git a/docs/docs/img/kitchensink.png b/docs/devguide/labs/kitchensink.png similarity index 100% rename from docs/docs/img/kitchensink.png rename to docs/devguide/labs/kitchensink.png diff --git a/docs/docs/img/tutorial/metadataWorkflowPost.png b/docs/devguide/labs/metadataWorkflowPost.png similarity index 100% rename from docs/docs/img/tutorial/metadataWorkflowPost.png rename to docs/devguide/labs/metadataWorkflowPost.png diff --git a/docs/docs/img/tutorial/metadataWorkflowRun.png b/docs/devguide/labs/metadataWorkflowRun.png similarity index 100% rename from docs/docs/img/tutorial/metadataWorkflowRun.png rename to docs/devguide/labs/metadataWorkflowRun.png diff --git a/docs/docs/img/tutorial/uiWorkflowDefinition.png b/docs/devguide/labs/uiWorkflowDefinition.png similarity index 100% rename from docs/docs/img/tutorial/uiWorkflowDefinition.png rename to docs/devguide/labs/uiWorkflowDefinition.png diff --git a/docs/docs/img/tutorial/uiWorkflowDefinitionVisual.png b/docs/devguide/labs/uiWorkflowDefinitionVisual.png similarity index 100% rename from docs/docs/img/tutorial/uiWorkflowDefinitionVisual.png rename to docs/devguide/labs/uiWorkflowDefinitionVisual.png diff --git a/docs/docs/img/tutorial/workflowLoaded.png b/docs/devguide/labs/workflowLoaded.png similarity index 100% rename from docs/docs/img/tutorial/workflowLoaded.png rename to docs/devguide/labs/workflowLoaded.png diff --git a/docs/docs/img/tutorial/workflowRunIdCopy.png b/docs/devguide/labs/workflowRunIdCopy.png similarity index 100% rename from docs/docs/img/tutorial/workflowRunIdCopy.png rename to docs/devguide/labs/workflowRunIdCopy.png diff --git a/docs/docs/img/tutorial/conductorUI.png b/docs/devguide/running/conductorUI.png similarity index 100% rename from docs/docs/img/tutorial/conductorUI.png rename to docs/devguide/running/conductorUI.png diff --git a/docs/docs/gettingstarted/docker.md b/docs/devguide/running/docker.md similarity index 69% rename from docs/docs/gettingstarted/docker.md rename to docs/devguide/running/docker.md index 483a57037..5cb2d7c69 100644 --- a/docs/docs/gettingstarted/docker.md +++ b/docs/devguide/running/docker.md @@ -1,5 +1,5 @@ -# Running Conductor using Docker +# Running Conductor Using Docker In this article we will explore how you can set up Netflix Conductor on your local machine using Docker compose. The docker compose will bring up the following: @@ -43,14 +43,14 @@ Once up and running, you will see the following in your Docker dashboard: You can access the UI & Server on your browser to verify that they are running correctly: #### Conductor Server URL -[http://localhost:8080](http://localhost:8080) +[{{ server_host }}]({{ server_host }}) - +![swagger](swagger.png) #### Conductor UI URL [http://localhost:5000/](http://localhost:5000) - +![conductor ui](conductorUI.png) ### 4. Exiting Compose @@ -81,7 +81,7 @@ To build and run the server image, without using `docker-compose`, from the `doc docker build -t conductor:server -f server/Dockerfile ../ docker run -p 8080:8080 -d --name conductor_server conductor:server ``` -This builds the image `conductor:server` and runs it in a container named `conductor_server`. The API should now be accessible at `localhost:8080`. +This builds the image `conductor:server` and runs it in a container named `conductor_server`. The API should now be accessible at `{{ server_host }}`. To 'login' to the running container, use the command: ``` @@ -120,56 +120,55 @@ docker build -t conductor:serverAndUI -f serverAndUI/Dockerfile ../ - With interal DB: `docker run -p 8080:8080 -p 80:5000 -d -t conductor:serverAndUI` - With external DB: `docker run -p 8080:8080 -p 80:5000 -d -t -e "CONFIG_PROP=config.properties" conductor:serverAndUI` +## Elasticsearch +Elasticsearch is optional, please be aware that disable it will make most of the conductor UI not functional. +### How to enable Elasticsearch +* Set `conductor.indexing.enabled=true` in your_config.properties +* Add config related to elasticsearch + E.g.: `conductor.elasticsearch.url=http://es:9200` -## Potential problem when using Docker Images - -#### Not enough memory - - 1. You will need at least 16 GB of memory to run everything. You can modify the docker compose to skip using - Elasticsearch if you have no option to run this with your memory options. - 2. To disable Elasticsearch using Docker Compose - follow the steps listed here: **TODO LINK** +### How to disable Elasticsearch +* Set `conductor.indexing.enabled=false` in your_config.properties +* Comment out all the config related to elasticsearch +E.g.: `conductor.elasticsearch.url=http://es:9200` -#### Elasticsearch fails to come up in arm64 based CPU machines - 1. As of writing this article, Conductor relies on 6.8.x version of Elasticsearch. This version doesn't have an - arm64 based Docker image. You will need to use Elasticsearch 7.x which requires a bit of customization to get up - and running +## Troubleshooting +To troubleshoot a failed startup, check the server logss located at `/app/logs` (default directory in dockerfile) -#### Elasticsearch remains in yellow health state +## Potential problem when using Docker Images -When you run Elasticsearch, sometimes the health remains in the *yellow* state. Conductor server by default requires -*green* state to run when indexing is enabled. To work around this, you can use the following property: `conductor.elasticsearch.clusterHealthColor=yellow`. +### Not enough memory +You will need at least 16 GB of memory to run everything. You can modify the docker compose to skip using +Elasticsearch if you have no option to run this with your memory options. -Reference: [Issue 2262][issue2262] +To disable Elasticsearch using Docker Compose - follow the steps above. -#### Elasticsearch timeout +### Elasticsearch fails to come up in arm64 based CPU machines +As of writing this article, Conductor relies on 6.8.x version of Elasticsearch. This version doesn't have an +arm64 based Docker image. You will need to use Elasticsearch 7.x which requires a bit of customization to get up +and running -By default, a standalone (single node) Elasticsearch has a *yellow* status which will cause timeout (`java.net.SocketTimeoutException`) for Conductor server (required status is *green*). -Spin up a cluster (more than one node) to prevent the timeout or use config option `conductor.elasticsearch.clusterHealthColor=yellow`. +### Elasticsearch remains in Yellow health -Reference: [Issue 2262][issue2262] +When you run Elasticsearch, sometimes the health remains in Yellow state. Conductor server by default requires +Green state to run when indexing is enabled. To work around this, you can use the following property: +`conductor.elasticsearch.clusterHealthColor=yellow` -#### Changes in config-*.properties do not take effect -Config is copy into image during docker build. You have to rebuild the image or better, link a volume to it to reflect new changes. +See Github [issue](https://github.com/Netflix/conductor/issues/2262) -#### To troubleshoot a failed startup -Check the log of the server, which is located at `/app/logs` (default directory in dockerfile) +### Elasticsearch timeout +Symptom: Standalone (single node) Elasticsearch has a yellow status which will cause timeout for the Conductor server at startup (Required: Green). -#### Unable to access to conductor:server API on port 8080 -It may takes some time for conductor server to start. Please check server log for potential error. +Solution: Spin up a cluster (more than one node) to prevent the timeout or use config option `conductor.elasticsearch.clusterHealthColor=yellow`. -#### Elasticsearch -Elasticsearch is optional, please be aware that disable it will make most of the conductor UI not functional. +See Github [issue](https://github.com/Netflix/conductor/issues/2262) -##### How to enable Elasticsearch -* Set `conductor.indexing.enabled=true` in your_config.properties -* Add config related to elasticsearch - E.g.: `conductor.elasticsearch.url=http://es:9200` +### Changes in config-*.properties do not take effect +Config is copied into the image during the docker build. You have to rebuild the image or better, link a volume to it +to reflect new changes automatically. -##### How to disable Elasticsearch -* Set `conductor.indexing.enabled=false` in your_config.properties -* Comment out all the config related to elasticsearch -E.g.: `conductor.elasticsearch.url=http://es:9200` +### Unable to access to conductor:server API on port 8080 +It may takes some time for conductor server to start. Please check server log for errors. -[issue2262]: https://github.com/Netflix/conductor/issues/2262 diff --git a/docs/docs/gettingstarted/hosted.md b/docs/devguide/running/hosted.md similarity index 100% rename from docs/docs/gettingstarted/hosted.md rename to docs/devguide/running/hosted.md diff --git a/docs/docs/gettingstarted/source.md b/docs/devguide/running/source.md similarity index 85% rename from docs/docs/gettingstarted/source.md rename to docs/devguide/running/source.md index f3a478656..97a0f4a98 100644 --- a/docs/docs/gettingstarted/source.md +++ b/docs/devguide/running/source.md @@ -5,7 +5,7 @@ In this article we will explore how you can set up Netflix Conductor on your loc features. ### Prerequisites -1. JDK 17 or greater +1. JDK 11 or greater 2. (Optional) Docker if you want to run tests. You can install docker from [here](https://www.docker.com/get-started/). 3. Node for building and running UI. Instructions at [https://nodejs.org](https://nodejs.org). 4. Yarn for building and running UI. Instructions at [https://classic.yarnpkg.com/en/docs/install](https://classic.yarnpkg.com/en/docs/install). @@ -48,9 +48,9 @@ server $ ../gradlew bootRun ``` Navigate to the swagger API docs: -[http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config](http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config) +[http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config) - +![swagger](swagger.png) ## Download and Run As an alternative to building from source, you can download and run the pre-compiled JAR. @@ -61,7 +61,7 @@ export REPO_URL=https://repo1.maven.org/maven2/com/netflix/conductor/conductor-s curl $REPO_URL/$CONDUCTOR_VER/conductor-server-$CONDUCTOR_VER-boot.jar \ --output conductor-server-$CONDUCTOR_VER-boot.jar; java -jar conductor-server-$CONDUCTOR_VER-boot.jar ``` -Navigate to the swagger URL: [http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config](http://localhost:8080/swagger-ui/index.html?configUrl=/api-docs/swagger-config) +Navigate to the swagger URL: [http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config) @@ -87,7 +87,7 @@ ui $ yarn run start Launch UI [http://localhost:5000](http://localhost:5000) - +![conductor ui](conductorUI.png) ## Summary 1. By default in-memory persistence is used, so any workflows created or excuted will be wiped out once the server is terminated. diff --git a/docs/docs/img/tutorial/swagger.png b/docs/devguide/running/swagger.png similarity index 100% rename from docs/docs/img/tutorial/swagger.png rename to docs/devguide/running/swagger.png diff --git a/docs/docs/apispec.md b/docs/docs/apispec.md deleted file mode 100644 index 94df57fca..000000000 --- a/docs/docs/apispec.md +++ /dev/null @@ -1,173 +0,0 @@ -# API Specification - -## Task & Workflow Metadata -| Endpoint | Description | Input | -|------------------------------------------|:---------------------------------|-------------------------------------------------------------| -| `GET /metadata/taskdefs` | Get all the task definitions | n/a | -| `GET /metadata/taskdefs/{taskType}` | Retrieve task definition | Task Name | -| `POST /metadata/taskdefs` | Register new task definitions | List of [Task Definitions](/configuration/taskdef.html) | -| `PUT /metadata/taskdefs` | Update a task definition | A [Task Definition](/configuration/taskdef.html) | -| `DELETE /metadata/taskdefs/{taskType}` | Delete a task definition | Task Name | -||| -| `GET /metadata/workflow` | Get all the workflow definitions | n/a | -| `POST /metadata/workflow` | Register new workflow | [Workflow Definition](/configuration/workflowdef.html) | -| `PUT /metadata/workflow` | Register/Update new workflows | List of [Workflow Definition](/configuration/workflowdef.html) | -| `GET /metadata/workflow/{name}?version=` | Get the workflow definitions | workflow name, version (optional) | -||| - -## Start A Workflow -### With Input only -See [Start Workflow Request](/gettingstarted/startworkflow.html). - -#### Output -Id of the workflow (GUID) - -### With Input and Task Domains -``` -POST /workflow -{ - //JSON payload for Start workflow request -} -``` -#### Start workflow request -JSON for start workflow request -``` -{ - "name": "myWorkflow", // Name of the workflow - "version": 1, // Version - “correlationId”: “corr1”, // correlation Id - "priority": 1, // Priority - "input": { - // Input map. - }, - "taskToDomain": { - // Task to domain map - } -} -``` - -#### Output -Id of the workflow (GUID) - - -## Retrieve Workflows -| Endpoint | Description | -|-----------------------------------------------------------------------------|-----------------------------------------------| -| `GET /workflow/{workflowId}?includeTasks=true | false` |Get Workflow State by workflow Id. If includeTasks is set, then also includes all the tasks executed and scheduled.| -| `GET /workflow/running/{name}` | Get all the running workflows of a given type | -| `GET /workflow/running/{name}/correlated/{correlationId}?includeClosed=true | false&includeTasks=true |false`|Get all the running workflows filtered by correlation Id. If includeClosed is set, also includes workflows that have completed running.| -| `GET /workflow/search` | Search for workflows. See Below. | - - -## Search for Workflows -Conductor uses Elasticsearch for indexing workflow execution and is used by search APIs. - -`GET /workflow/search?start=&size=&sort=&freeText=&query=` - -| Parameter | Description | -|-----------|------------------------------------------------------------------------------------------------------------------| -| start | Page number. Defaults to 0 | -| size | Number of results to return | -| sort | Sorting. Format is: `ASC:` or `DESC:` to sort in ascending or descending order by a field | -| freeText | Elasticsearch supported query. e.g. workflowType:"name_of_workflow" | -| query | SQL like where clause. e.g. workflowType = 'name_of_workflow'. Optional if freeText is provided. | - -### Output -Search result as described below: -```json -{ - "totalHits": 0, - "results": [ - { - "workflowType": "string", - "version": 0, - "workflowId": "string", - "correlationId": "string", - "startTime": "string", - "updateTime": "string", - "endTime": "string", - "status": "RUNNING", - "input": "string", - "output": "string", - "reasonForIncompletion": "string", - "executionTime": 0, - "event": "string" - } - ] -} -``` - -## Manage Workflows -| Endpoint | Description | -|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------| -| `PUT /workflow/{workflowId}/pause` | Pause. No further tasks will be scheduled until resumed. Currently running tasks are not paused. | -| `PUT /workflow/{workflowId}/resume` | Resume normal operations after a pause. | -| `POST /workflow/{workflowId}/rerun` | See Below. | -| `POST /workflow/{workflowId}/restart` | Restart workflow execution from the start. Current execution history is wiped out. | -| `POST /workflow/{workflowId}/retry` | Retry the last failed task. | -| `PUT /workflow/{workflowId}/skiptask/{taskReferenceName}` | See below. | -| `DELETE /workflow/{workflowId}` | Terminates the running workflow. | -| `DELETE /workflow/{workflowId}/remove` | Deletes the workflow from system. Use with caution. | - -### Rerun -Re-runs a completed workflow from a specific task. - -`POST /workflow/{workflowId}/rerun` - -```json -{ - "reRunFromWorkflowId": "string", - "workflowInput": {}, - "reRunFromTaskId": "string", - "taskInput": {} -} -``` - -###Skip Task - -Skips a task execution (specified as `taskReferenceName` parameter) in a running workflow and continues forward. -Optionally updating task's input and output as specified in the payload. -`PUT /workflow/{workflowId}/skiptask/{taskReferenceName}?workflowId=&taskReferenceName=` -```json -{ - "taskInput": {}, - "taskOutput": {} -} -``` - -## Manage Tasks -| Endpoint | Description | -|-------------------------------------------------------|-------------------------------------------------------| -| `GET /tasks/{taskId}` | Get task details. | -| `GET /tasks/queue/all` | List the pending task sizes. | -| `GET /tasks/queue/all/verbose` | Same as above, includes the size per shard | -| `GET /tasks/queue/sizes?taskType=&taskType=&taskType` | Return the size of pending tasks for given task types | -||| - -## Polling, Ack and Update Task -These are critical endpoints used to poll for task, send ack (after polling) and finally updating the task result by worker. - - -| Endpoint | Description | -|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `GET /tasks/poll/{taskType}?workerid=&domain=` | Poll for a task. `workerid` identifies the worker that polled for the job and `domain` allows the poller to poll for a task in a specific domain | -| `GET /tasks/poll/batch/{taskType}?count=&timeout=&workerid=&domain` | Poll for a task in a batch specified by `count`. This is a long poll and the connection will wait until `timeout` or if there is at-least 1 item available, whichever comes first.`workerid` identifies the worker that polled for the job and `domain` allows the poller to poll for a task in a specific domain | -| `POST /tasks` | Update the result of task execution. See the schema below. | - - -### Schema for updating Task Result -```json -{ - "workflowInstanceId": "Workflow Instance Id", - "taskId": "ID of the task to be updated", - "reasonForIncompletion" : "If failed, reason for failure", - "callbackAfterSeconds": 0, - "status": "IN_PROGRESS|FAILED|COMPLETED", - "outputData": { - //JSON document representing Task execution output - } - -} -``` -!!!Info "Acknowledging tasks after poll" - If the worker fails to ack the task after polling, the task is re-queued and put back in queue and is made available during subsequent poll. diff --git a/docs/docs/configuration/sysoperator.md b/docs/docs/configuration/sysoperator.md deleted file mode 100644 index 181d72fd3..000000000 --- a/docs/docs/configuration/sysoperator.md +++ /dev/null @@ -1,20 +0,0 @@ -# System Operators - -Operators are built-in primitives in Conductor that allow you to define the control flow in the workflow. -Operators are similar to programming constructs such as for loops, decisions, etc. -Conductor has support for most of the programing primitives allowing you to define the most advanced workflows. - -## Supported Operators -Conductor supports the following programming language constructs: - -| Language Construct | Conductor Operator | -|----------------------------------|-------------------------------------------------------------| -| Do/While or Loops | [Do While Task](/reference-docs/do-while-task.html) | -| Dynamic Fork | [Dynamic Fork Task](/reference-docs/dynamic-fork-task.html) | -| Fork / Parallel execution | [Fork Task](/reference-docs/fork-task.html) | -| Join | [Join Task](/reference-docs/join-task.html) | -| Sub Process / Sub-Flow | [Sub Workflow Task](/reference-docs/sub-workflow-task.html) | -| Switch//Decision/if..then...else | [Switch Task](/reference-docs/switch-task.html) | -| Terminate | [Terminate Task](/reference-docs/terminate-task.html) | -| Variables | [Variable Task](/reference-docs/set-variable-task.html) | -| Wait | [Wait Task](/reference-docs/wait-task.html) | diff --git a/docs/docs/configuration/systask.md b/docs/docs/configuration/systask.md deleted file mode 100644 index 4b2f964a6..000000000 --- a/docs/docs/configuration/systask.md +++ /dev/null @@ -1,256 +0,0 @@ -# System Tasks - -System Tasks (Workers) are built-in tasks that are general purpose and re-usable. They run on the Conductor servers. -Such tasks allow you to get started without having to write custom workers. - -## Available System Tasks - -Conductor has the following set of system tasks available. - -| Task | Description | Use Case | -|-----------------------|--------------------------------------------------------|------------------------------------------------------------------------------------| -| Event Publishing | [Event Task](/reference-docs/event-task.html) | External eventing system integration. e.g. amqp, sqs, nats | -| HTTP | [HTTP Task](/reference-docs/http-task.html) | Invoke any HTTP(S) endpoints | -| Inline Code Execution | [Inline Task](/reference-docs/inline-task.html) | Execute arbitrary lightweight javascript code | -| JQ Transform | [JQ Task](/reference-docs/json-jq-transform-task.html) | Use JQ to transform task input/output | -| Kafka Publish | [Kafka Task](/reference-docs/kafka-publish-task.html) | Publish messages to Kafka | - -| Name | Description | -|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| -| joinOn | List of task reference names, which the EXCLUSIVE_JOIN will lookout for to capture output. From above example, this could be ["T2", "T3"] | -| defaultExclusiveJoinTask | Task reference name, whose output should be used incase the decision case is undefined. From above example, this could be ["T1"] | - - -**Example** - -``` json -{ - "name": "exclusive_join", - "taskReferenceName": "exclusiveJoin", - "type": "EXCLUSIVE_JOIN", - "joinOn": [ - "task2", - "task3" - ], - "defaultExclusiveJoinTask": [ - "task1" - ] -} -``` - - -## Wait -A wait task is implemented as a gate that remains in ```IN_PROGRESS``` state unless marked as ```COMPLETED``` or ```FAILED``` by an external trigger. -To use a wait task, set the task type as ```WAIT``` - -**Parameters:** -None required. - -**External Triggers for Wait Task** - -Task Resource endpoint can be used to update the status of a task to a terminate state. - -Contrib module provides SQS integration where an external system can place a message in a pre-configured queue that the server listens on. As the messages arrive, they are marked as ```COMPLETED``` or ```FAILED```. - -**SQS Queues** - -* SQS queues used by the server to update the task status can be retrieve using the following API: -``` -GET /queue -``` -* When updating the status of the task, the message needs to conform to the following spec: - * Message has to be a valid JSON string. - * The message JSON should contain a key named ```externalId``` with the value being a JSONified string that contains the following keys: - * ```workflowId```: Id of the workflow - * ```taskRefName```: Task reference name that should be updated. - * Each queue represents a specific task status and tasks are marked accordingly. e.g. message coming to a ```COMPLETED``` queue marks the task status as ```COMPLETED```. - * Tasks' output is updated with the message. - -**Example SQS Payload:** - -```json -{ - "some_key": "valuex", - "externalId": "{\"taskRefName\":\"TASK_REFERENCE_NAME\",\"workflowId\":\"WORKFLOW_ID\"}" -} -``` - - -## Dynamic Task - -Dynamic Task allows to execute one of the registered Tasks dynamically at run-time. It accepts the task name to execute in inputParameters. - -**Parameters:** - -|name|description| -|---|---| -| dynamicTaskNameParam|Name of the parameter from the task input whose value is used to schedule the task. e.g. if the value of the parameter is ABC, the next task scheduled is of type 'ABC'.| - -**Example** -``` json -{ - "name": "user_task", - "taskReferenceName": "t1", - "inputParameters": { - "files": "${workflow.input.files}", - "taskToExecute": "${workflow.input.user_supplied_task}" - }, - "type": "DYNAMIC", - "dynamicTaskNameParam": "taskToExecute" -} -``` -If the workflow is started with input parameter user_supplied_task's value as __user_task_2__, Conductor will schedule __user_task_2__ when scheduling this dynamic task. - -## Inline Task - -Inline Task helps execute ad-hoc logic at Workflow run-time, using any evaluator engine. Supported evaluators -are `value-param` evaluator which simply translates the input parameter to output and `javascript` evaluator that -evaluates Javascript expression. - -This is particularly helpful in running simple evaluations in Conductor server, over creating Workers. - -**Parameters:** - -|name|type|description|notes| -|---|---|---|---| -|evaluatorType|String|Type of the evaluator. Supported evaluators: `value-param`, `javascript` which evaluates javascript expression.| -|expression|String|Expression associated with the type of evaluator. For `javascript` evaluator, Javascript evaluation engine is used to evaluate expression defined as a string. Must return a value.|Must be non-empty.| - -Besides `expression`, any value is accessible as `$.value` for the `expression` to evaluate. - -**Outputs:** - -|name|type|description| -|---|---|---| -|result|Map|Contains the output returned by the evaluator based on the `expression`| - -The task output can then be referenced in downstream tasks like: -```"${inline_test.output.result.testvalue}"``` - -**Example** -``` json -{ - "name": "INLINE_TASK", - "taskReferenceName": "inline_test", - "type": "INLINE", - "inputParameters": { - "inlineValue": "${workflow.input.inlineValue}", - "evaluatorType": "javascript", - "expression": "function scriptFun(){if ($.inlineValue == 1){ return {testvalue: true} } else { return - {testvalue: false} }} scriptFun();" - } -} -``` - -## Terminate Task - -Task that can terminate a workflow with a given status and modify the workflow's output with a given parameter. It can act as a "return" statement for conditions where you simply want to terminate your workflow. - -For example, if you have a decision where the first condition is met, you want to execute some tasks, otherwise you want to finish your workflow. - -**Parameters:** - -|name|type| description | notes | -|---|---|---------------------------------------------------|-------------------------| -|terminationStatus|String| can only accept "COMPLETED" or "FAILED" | task cannot be optional | - |terminationReason|String| reason for incompletion to be set in the workflow | optional | -|workflowOutput|Any| Expected workflow output | optional | - -**Outputs:** - -|name|type|description| -|---|---|---| -|output|Map|The content of `workflowOutput` from the inputParameters. An empty object if `workflowOutput` is not set.| - -```json -{ - "name": "terminate", - "taskReferenceName": "terminate0", - "inputParameters": { - "terminationStatus": "COMPLETED", - "terminationReason": "", - "workflowOutput": "${task0.output}" - }, - "type": "TERMINATE", - "startDelay": 0, - "optional": false -} -``` - - -## Kafka Publish Task - -A kafka Publish task is used to push messages to another microservice via kafka - -**Parameters:** - -The task expects an input parameter named ```kafka_request``` as part of the task's input with the following details: - -|name|description| -|---|---| -| bootStrapServers |bootStrapServers for connecting to given kafka.| -|key|Key to be published| -|keySerializer | Serializer used for serializing the key published to kafka. One of the following can be set :
1. org.apache.kafka.common.serialization.IntegerSerializer
2. org.apache.kafka.common.serialization.LongSerializer
3. org.apache.kafka.common.serialization.StringSerializer.
Default is String serializer | -|value| Value published to kafka| -|requestTimeoutMs| Request timeout while publishing to kafka. If this value is not given the value is read from the property `kafka.publish.request.timeout.ms`. If the property is not set the value defaults to 100 ms | -|maxBlockMs| maxBlockMs while publishing to kafka. If this value is not given the value is read from the property `kafka.publish.max.block.ms`. If the property is not set the value defaults to 500 ms | -|headers|A map of additional kafka headers to be sent along with the request.| -|topic|Topic to publish| - -The producer created in the kafka task is cached. By default the cache size is 10 and expiry time is 120000 ms. To change the defaults following can be modified kafka.publish.producer.cache.size,kafka.publish.producer.cache.time.ms respectively. - -**Kafka Task Output** - -Task status transitions to COMPLETED - -**Example** - -Task sample - -```json -{ - "name": "call_kafka", - "taskReferenceName": "call_kafka", - "inputParameters": { - "kafka_request": { - "topic": "userTopic", - "value": "Message to publish", - "bootStrapServers": "localhost:9092", - "headers": { - "x-Auth":"Auth-key" - }, - "key": "123", - "keySerializer": "org.apache.kafka.common.serialization.IntegerSerializer" - } - }, - "type": "KAFKA_PUBLISH" -} -``` - -The task is marked as ```FAILED``` if the message could not be published to the Kafka queue. - - -## Do While Task - -Sequentially execute a list of task as long as a condition is true. The list of tasks is executed first, before the condition is -checked (even for the first iteration). - -When scheduled, each task of this loop will see its `taskReferenceName` concatenated with `__i`, with `i` being the -iteration number, starting at 1. Warning: `taskReferenceName` containing arithmetic operators must not be used. - -Each task output is stored as part of the `DO_WHILE` task, indexed by the iteration value (see example below), allowing -the condition to reference the output of a task for a specific iteration (eg. ```$.LoopTask['iteration]['first_task']```) - -The `DO_WHILE` task is set to `FAILED` as soon as one of the loopTask fails. In such case retry, iteration starts from 1. - -Limitations: - - Domain or isolation group execution is unsupported; - - Nested `DO_WHILE` is unsupported; - - `SUB_WORKFLOW` is unsupported; - - Since loopover tasks will be executed in loop inside scope of parent do while task, crossing branching outside of DO_WHILE - task is not respected. Branching inside loopover task is supported. - -**Parameters:** - -|name|type|description| - diff --git a/docs/docs/configuration/taskdef.md b/docs/docs/configuration/taskdef.md deleted file mode 100644 index 075912ff4..000000000 --- a/docs/docs/configuration/taskdef.md +++ /dev/null @@ -1,112 +0,0 @@ -# Task Definition - -Tasks are the building blocks of workflow in Conductor. A task can be an operator, system task or custom code written in any programming language. - -A typical Conductor workflow is a list of tasks that are executed until completion or the termination of the workflow. - -Conductor maintains a registry of worker tasks. A task MUST be registered before being used in a workflow. - -**Example** -``` json -{ - "name": "encode_task", - "retryCount": 3, - - "timeoutSeconds": 1200, - "pollTimeoutSeconds": 3600, - "inputKeys": [ - "sourceRequestId", - "qcElementType" - ], - "outputKeys": [ - "state", - "skipped", - "result" - ], - "timeoutPolicy": "TIME_OUT_WF", - "retryLogic": "FIXED", - "retryDelaySeconds": 600, - "responseTimeoutSeconds": 1200, - "concurrentExecLimit": 100, - "rateLimitFrequencyInSeconds": 60, - "rateLimitPerFrequency": 50, - "ownerEmail": "foo@bar.com", - "description": "Sample Encoding task" -} -``` - -| Field | Description | Notes | -|----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------| -| name | Task Type. Unique name of the Task that resonates with it's function. | Unique | -| description | Description of the task | optional | -| retryCount | No. of retries to attempt when a Task is marked as failure | defaults to 3 | -| retryLogic | Mechanism for the retries | [Retry Logic values](#retry-logic) | -| retryDelaySeconds | Time to wait before retries | defaults to 60 seconds | -| timeoutPolicy | Task's timeout policy | [timeout policy values](#timeout-policy) | -| timeoutSeconds | Time in seconds, after which the task is marked as `TIMED_OUT` if not completed after transitioning to `IN_PROGRESS` status for the first time | No timeouts if set to 0 | -| pollTimeoutSeconds | Time in seconds, after which the task is marked as `TIMED_OUT` if not polled by a worker | No timeouts if set to 0 | -| responseTimeoutSeconds | Must be greater than 0 and less than timeoutSeconds. The task is rescheduled if not updated with a status after this time (heartbeat mechanism). Useful when the worker polls for the task but fails to complete due to errors/network failure. | defaults to 3600 | -| backoffScaleFactor | Must be greater than 0. Scale factor for linearity of the backoff | defaults to 1 | -| inputKeys | Array of keys of task's expected input. Used for documenting task's input. See [Using inputKeys and outputKeys](#using-inputkeys-and-outputkeys). | optional | -| outputKeys | Array of keys of task's expected output. Used for documenting task's output | optional | -| inputTemplate | See [Using inputTemplate](#using-inputtemplate) below. | optional | -| concurrentExecLimit | Number of tasks that can be executed at any given time. | optional | -| rateLimitFrequencyInSeconds, rateLimitPerFrequency | See [Task Rate limits](#task-rate-limits) below. | optional | - - -### Retry Logic - -* FIXED : Reschedule the task after the ```retryDelaySeconds``` -* EXPONENTIAL_BACKOFF : Reschedule after ```retryDelaySeconds 2^(attemptNumber)``` -* LINEAR_BACKOFF : Reschedule after ```retryDelaySeconds * backoffRate * attemptNumber``` - -### Timeout Policy - -* RETRY : Retries the task again -* TIME_OUT_WF : Workflow is marked as TIMED_OUT and terminated -* ALERT_ONLY : Registers a counter (task_timeout) - -### Task Concurrent Execution Limits - -* `concurrentExecLimit` limits the number of simultaneous Task executions at any point. -**Example:** -If you have 1000 task executions waiting in the queue, and 1000 workers polling this queue for tasks, but if you have set `concurrentExecLimit` to 10, only 10 tasks would be given to workers (which would lead to starvation). If any of the workers finishes execution, a new task(s) will be removed from the queue, while still keeping the current execution count to 10. - -### Task Rate limits - -> Note: Rate limiting is only supported for the Redis-persistence module and is not available with other persistence layers. - -* `rateLimitFrequencyInSeconds` and `rateLimitPerFrequency` should be used together. -* `rateLimitFrequencyInSeconds` sets the "frequency window", i.e the `duration` to be used in `events per duration`. Eg: 1s, 5s, 60s, 300s etc. -* `rateLimitPerFrequency`defines the number of Tasks that can be given to Workers per given "frequency window". - -**Example:** -Let's set `rateLimitFrequencyInSeconds = 5`, and `rateLimitPerFrequency = 12`. This means, our frequency window is of 5 seconds duration, and for each frequency window, Conductor would only give 12 tasks to workers. So, in a given minute, Conductor would only give 12*(60/5) = 144 tasks to workers irrespective of the number of workers that are polling for the task. -Note that unlike `concurrentExecLimit`, rate limiting doesn't take into account tasks already in progress/completed. Even if all the previous tasks are executed within 1 sec, or would take a few days, the new tasks are still given to workers at configured frequency, 144 tasks per minute in above example. - - -### Using inputKeys and outputKeys - -* `inputKeys` and `outputKeys` can be considered as parameters and return values for the Task. -* Consider the task Definition as being represented by an interface: ```(value1, value2 .. valueN) someTaskDefinition(key1, key2 .. keyN);``` -* However, these parameters are not strictly enforced at the moment. Both `inputKeys` and `outputKeys` act as a documentation for task re-use. The tasks in workflow need not define all of the keys in the task definition. -* In the future, this can be extended to be a strict template that all task implementations must adhere to, just like interfaces in programming languages. - -### Using inputTemplate - -* `inputTemplate` allows to define default values, which can be overridden by values provided in Workflow. -* Eg: In your Task Definition, you can define your inputTemplate as: - -```json -"inputTemplate": { - "url": "https://some_url:7004" -} -``` - -* Now, in your workflow Definition, when using above task, you can use the default `url` or override with something else in the task's `inputParameters`. - -```json -"inputParameters": { - "url": "${workflow.input.some_new_url}" -} -``` diff --git a/docs/docs/configuration/workerdef.md b/docs/docs/configuration/workerdef.md deleted file mode 100644 index 7081f989f..000000000 --- a/docs/docs/configuration/workerdef.md +++ /dev/null @@ -1,14 +0,0 @@ -# Worker Definition - -A worker is responsible for executing a task. Operator and System tasks are handled by the Conductor server, while user -defined tasks needs to have a worker created that awaits the work to be scheduled by the server for it to be executed. -Workers can be implemented in any language, and Conductor provides support for Java, Golang and Python worker framework that provides features such as -polling threads, metrics and server communication that makes creating workers easy. - -Each worker embodies Microservice design pattern and follows certain basic principles: - -1. Workers are stateless and do not implement a workflow specific logic. -2. Each worker executes a very specific task and produces well defined output given specific inputs. -3. Workers are meant to be idempotent (or should handle cases where the task that partially executed gets rescheduled due to timeouts etc.) -4. Workers do not implement the logic to handle retries etc, that is taken care by the Conductor server. - diff --git a/docs/docs/configuration/workflowdef.md b/docs/docs/configuration/workflowdef.md deleted file mode 100644 index 35e167ac5..000000000 --- a/docs/docs/configuration/workflowdef.md +++ /dev/null @@ -1,229 +0,0 @@ -# Workflow Definition - -## What are Workflows? - -At a high level, a workflow is the Conductor primitive that encompasses the definition and flow of your business logic. -A workflow is a collection (graph) of tasks and sub-workflows. A workflow definition specifies the order of execution of -these [Tasks](taskdef.md). It also specifies how data/state is passed from one task to the other (using the -input/output parameters). These are then combined to give you the final result. This orchestration of Tasks can -happen in a hybrid ecosystem that includes microservices, serverless functions, and monolithic applications. They can -also span across any public cloud and on-premise data center footprints. In addition, the orchestration of tasks can be -across any programming language since Conductor is also language agnostic. - -One key benefit of this approach is that you can build a complex application using simple and granular tasks that do not -need to be aware of or keep track of the state of your application's execution flow. Conductor keeps track of the state, -calls tasks in the right order (sequentially or in parallel, as defined by you), retry calls if needed, handle failure -scenarios gracefully, and outputs the final result. - -Leveraging workflows in Conductor enables developers to truly focus on their core mission - building their application -code in the languages of their choice. Conductor does the heavy lifting associated with ensuring high -reliability, transactional consistency, and long durability of their workflows. Simply put, wherever your application's -component lives and whichever languages they were written in, you can build a workflow in Conductor to orchestrate their -execution in a reliable & scalable manner. - -## What does a Workflow look like? - -Let's start with a basic workflow and understand what are the different aspects of it. In particular, we will talk about -two stages of a workflow, *defining* a workflow and *executing* a workflow - -### Simple Workflow Example - -Assume your business logic is to simply to get some shipping information and then do the shipping. You start by -logically partitioning them into two tasks: - -* **shipping_info** -* **shipping_task** - -First we would build these two task definitions. Let's assume that ```shipping info``` takes an account number, and returns a name and address. - -**Example** -```json -{ - "name": "mail_a_box", - "description": "shipping Workflow", - "version": 1, - "tasks": [ - { - "name": "shipping_info", - "taskReferenceName": "shipping_info_ref", - "inputParameters": { - "account": "${workflow.input.accountNumber}" - }, - "type": "SIMPLE" - }, - { - "name": "shipping_task", - "taskReferenceName": "shipping_task_ref", - "inputParameters": { - "name": "${shipping_info_ref.output.name}", - "streetAddress": "${shipping_info_ref.output.streetAddress}", - "city": "${shipping_info_ref.output.city}", - "state": "${shipping_info_ref.output.state}", - "zipcode": "${shipping_info_ref.output.zipcode}", - }, - "type": "SIMPLE" - } - ], - "outputParameters": { - "trackingNumber": "${shipping_task_ref.output.trackinNumber}" - }, - "failureWorkflow": "shipping_issues", - "restartable": true, - "workflowStatusListenerEnabled": true, - "ownerEmail": "conductor@example.com", - "timeoutPolicy": "ALERT_ONLY", - "timeoutSeconds": 0, - "variables": {}, - "inputTemplate": {} -} -``` - -The mail_a_box workflow has 2 tasks: - 1. The first task takes the provided account number, and outputs an address. - 2. The 2nd task takes the address info and generates a shipping label. - - Upon completion of the 2 tasks, the workflow outputs the tracking number generated in the 2nd task. If the workflow fails, a second workflow named ```shipping_issues``` is run. - -## Fields in a Workflow - -| Field | Description | Notes | -|:------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------| -| name | Name of the workflow || -| description | Description of the workflow | optional | -| version | Numeric field used to identify the version of the schema. Use incrementing numbers | When starting a workflow execution, if not specified, the definition with highest version is used | -| tasks | An array of task definitions. | [Task properties](#tasks-within-workflow) | -| inputParameters | List of input parameters. Used for documenting the required inputs to workflow | optional | -| inputTemplate | Default input values. See [Using inputTemplate](#using-inputtemplate) | optional | -| outputParameters | JSON template used to generate the output of the workflow | If not specified, the output is defined as the output of the _last_ executed task | -| failureWorkflow | String; Workflow to be run on current Workflow failure. Useful for cleanup or post actions on failure. | optional | -| schemaVersion | Current Conductor Schema version. schemaVersion 1 is discontinued. | Must be 2 | -| restartable | Boolean flag to allow Workflow restarts | defaults to true | -| workflowStatusListenerEnabled | If true, every workflow that gets terminated or completed will send a notification. See [workflow notifictions](#workflow-notifications) | optional (false by default) | - -## Tasks within Workflow -```tasks``` property in a workflow execution defines an array of tasks to be executed in that order. - -| Field | Description | Notes | -|:------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------| -| name | Name of the task. MUST be registered as a task with Conductor before starting the workflow || -| taskReferenceName | Alias used to refer the task within the workflow. MUST be unique within workflow. || -| type | Type of task. SIMPLE for tasks executed by remote workers, or one of the system task types || -| description | Description of the task | optional | -| optional | true or false. When set to true - workflow continues even if the task fails. The status of the task is reflected as `COMPLETED_WITH_ERRORS` | Defaults to `false` | -| inputParameters | JSON template that defines the input given to the task | See [Wiring Inputs and Outputs](#wiring-inputs-and-outputs) for details | -| domain | See [Task Domains](/configuration/taskdomains.html) for more information. | optional | - -In addition to these parameters, System Tasks have their own parameters. Checkout [System Tasks](/configuration/systask.html) for more information. - -## Wiring Inputs and Outputs - -Workflows are supplied inputs by client when a new execution is triggered. -Workflow input is a JSON payload that is available via ```${workflow.input...}``` expressions. - -Each task in the workflow is given input based on the ```inputParameters``` template configured in workflow definition. ```inputParameters``` is a JSON fragment with value containing parameters for mapping values from input or output of a workflow or another task during the execution. - -Syntax for mapping the values follows the pattern as: - -__${SOURCE.input/output.JSONPath}__ - -| field | description | -|--------------|--------------------------------------------------------------------------| -| SOURCE | can be either "workflow" or any of the task reference name | -| input/output | refers to either the input or output of the source | -| JSONPath | JSON path expression to extract JSON fragment from source's input/output | - - -!!! note "JSON Path Support" - Conductor supports [JSONPath](http://goessner.net/articles/JsonPath/) specification and uses Java implementation from [here](https://github.com/jayway/JsonPath). - -!!! note "Escaping expressions" - To escape an expression, prefix it with an extra _$_ character (ex.: ```$${workflow.input...}```). - -**Example** - -Consider a task with input configured to use input/output parameters from workflow and a task named __loc_task__. - -```json -{ - "inputParameters": { - "movieId": "${workflow.input.movieId}", - "url": "${workflow.input.fileLocation}", - "lang": "${loc_task.output.languages[0]}", - "http_request": { - "method": "POST", - "url": "http://example.com/${loc_task.output.fileId}/encode", - "body": { - "recipe": "${workflow.input.recipe}", - "params": { - "width": 100, - "height": 100 - } - }, - "headers": { - "Accept": "application/json", - "Content-Type": "application/json" - } - } - } -} -``` - -Consider the following as the _workflow input_ - -```json -{ - "movieId": "movie_123", - "fileLocation":"s3://moviebucket/file123", - "recipe":"png" -} -``` -And the output of the _loc_task_ as the following; - -```json -{ - "fileId": "file_xxx_yyy_zzz", - "languages": ["en","ja","es"] -} -``` - -When scheduling the task, Conductor will merge the values from workflow input and loc_task's output and create the input to the task as follows: - -```json -{ - "movieId": "movie_123", - "url": "s3://moviebucket/file123", - "lang": "en", - "http_request": { - "method": "POST", - "url": "http://example.com/file_xxx_yyy_zzz/encode", - "body": { - "recipe": "png", - "params": { - "width": 100, - "height": 100 - } - }, - "headers": { - "Accept": "application/json", - "Content-Type": "application/json" - } - } -} -``` - -### Using inputTemplate - -* `inputTemplate` allows to define default values, which can be overridden by values provided in Workflow. -* Eg: In your Workflow Definition, you can define your inputTemplate as: - -```json -"inputTemplate": { - "url": "https://some_url:7004" -} -``` - -And `url` would be `https://some_url:7004` if no `url` was provided as input to your workflow. - -## Workflow notifications - -Conductor can be configured to publish notifications to external systems upon completion/termination of workflows. See [extending conductor](/extend.html) for details. diff --git a/docs/docs/css/custom.css b/docs/docs/css/custom.css deleted file mode 100644 index 3e4728cd5..000000000 --- a/docs/docs/css/custom.css +++ /dev/null @@ -1,313 +0,0 @@ -:root { - /*--main-text-color: #212121;*/ - --brand-blue: #1976d2; - --brand-dark-blue: #242A36; - --caption-color: #4f4f4f; - --brand-lt-blue: #f0f5fb; - --brand-gray: rgb(118, 118, 118); - --brand-lt-gray: rgb(203,204,207); - --brand-red: #e50914; -} -body { - color: var(--brand-dark-blue); - font-family: "Roboto", sans-serif; - font-weight: 400; -} - -body::before { - background: none; - display: none; -} -body > .container { - padding-top: 30px; -} - -.bg-primary { - background: #fff !important; -} - -/* Navbar */ -.navbar { - box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%), 0 0 2px 0 rgb(0 0 0 / 10%); - padding-left: 30px; - padding-right: 30px; - height: 80px; -} -.navbar-brand { - background-image: url(/img/logo.svg); - background-size: cover; - color: transparent !important; - padding: 0; - text-shadow: none; - margin-top: -6px; - height: 37px; - width: 175px; -} -.navbar-nav { - margin-left: 50px; -} -.navbar-nav > .navitem, .navbar-nav > .dropdown { - margin-left: 30px; -} -.navbar-nav > li .nav-link{ - font-size: 15px; -} - -.navbar-nav .nav-link { - color: #242A36 !important; - font-family: "Inter"; - font-weight: 700; -} - -.navbar-nav.ml-auto > li:first-child { - display: none; -} -.navbar-nav.ml-auto .nav-link{ - font-size: 0px; -} -.navbar-nav.ml-auto .nav-link .fa{ - font-size: 30px; -} -.navbar-nav .dropdown-item { - color: var(--brand-dark-blue); - font-family: "Inter"; - font-weight: 500; - font-size: 14px; - background-color: transparent; -} -.navbar-nav .dropdown-menu > li:hover { - background-color: var(--brand-blue); -} -.navbar-nav .dropdown-menu > li:hover > .dropdown-item { - color: #fff; -} -.navbar-nav .dropdown-submenu:hover > .dropdown-item { - background-color: var(--brand-blue); -} - - -.navbar-nav .dropdown-menu li { - margin: 0px; - padding-top: 5px; - padding-bottom: 5px; -} -.navbar-nav .dropdown-item.active { - background-color: transparent; -} - -.brand-darkblue { - background: #242A36 !important; -} - -.brand-gray { - background: rgb(245,245,245); -} -.brand-blue { - background: #1976D2; -} -.brand-white { - background: #fff; -} -.logo { - height: 444px; -} - -/* Fonts */ -h1, h2, h3, h4, h5, h6 { - color: var(--brand-dark-blue); - margin-bottom: 20px; -} -h1:first-child { - margin-top: 0; -} - -h1 { - font-family: "Inter", sans-serif; - font-size: 32px; - font-weight: 700; - margin-top: 50px; -} - -h2 { - font-family: "Inter", sans-serif; - font-size: 24px; - font-weight: 700; - margin-top: 40px; -} - -h3 { - font-family: "Roboto", sans-serif; - font-size: 20px; - font-weight: 500; - margin-top: 30px; -} - -h4 { - font-family: "Roboto", sans-serif; - font-size: 18px; - font-weight: 400; - margin-top: 20px; -} - -.main li { - margin-bottom: 15px; -} - - -.btn { - font-family: "Roboto", sans-serif; - font-size: 14px; -} -.btn-primary { - background: #1976D2; - border: none; -} - -.hero { - padding-top: 100px; - padding-bottom: 100px; -} - -.hero .heading { - font-size: 56px; - font-weight: 900; - line-height: 68px; -} - -.hero .btn { - font-size: 16px; - padding: 10px 20px; -} - -.hero .illustration { - margin-left: 35px; -} - - -.bullets .heading, .module .heading { - font-family: "Inter", sans-serif; - font-size: 26px; - font-weight: 700; -} -.bullets .row { - margin-bottom: 60px; -} -.bullets .caption { - padding-top: 10px; - padding-right: 30px; -} -.icon { - height: 25px; - margin-right: 5px; - vertical-align: -3px; -} - -.caption { - font-weight: 400; - font-size: 17px; - line-height: 24px; - color: var(--caption-color); -} - -.module { - margin-top: 80px; - margin-bottom: 80px; - padding-top: 50px; - padding-bottom: 50px; -} - -.module .caption { - padding-top: 10px; - padding-right: 80px; -} -.module .screenshot { - width: 600px; - height: 337px; - box-shadow:inset 0 1px 0 rgba(255,255,255,.6), 0 22px 70px 4px rgba(0,0,0,0.56), 0 0 0 1px rgba(0, 0, 0, 0.0); - border-radius: 5px; - background-size: cover; -} - -/* Footer */ -footer { - margin: 0px; - padding: 0px !important; - text-align: left; - font-weight: 400; -} -.footer { - background-color: var(--brand-dark-blue); - padding: 50px 0px; - color: #fff; - font-size: 14px; - margin-top: 50px; -} -.footer a { - color: var(--brand-lt-gray); -} -.footer .subhead { - font-weight: 700; - color: #fff; - font-size: 15px; - margin-bottom: 10px; -} -.footer .red { - color: var(--brand-red); -} -.footer .fr { - text-align: right; -} - -/* TOC menu */ -.toc ul { - list-style: none; - padding: 0px; -} -.toc > ul > li li { - padding-left: 15px; - font-weight: 400; - font-size: 14px; -} -.toc > ul > li { - font-size: 15px; - font-weight: 500; -} -.toc .toc-link { - margin-bottom: 5px; - display: block; - color: var(--brand-dark-blue); -} -.toc .toc-link.active { - font-weight: 700; -} - -/* Homepage Overrides */ -.homepage > .container { - max-width: none; -} -.homepage .toc { - display: none; -} - -/* Comparison block */ -.compare { - background-color: var(--brand-lt-blue); - padding-top: 80px; - padding-bottom: 80px; - margin: 0px -15px; -} -.compare .heading { - margin-bottom: 30px; - margin-top: 0px; -} -.compare .bubble { - background: #fff; - border-radius: 10px; - padding: 30px; - height: 100%; -} - -.compare .caption { - font-size: 15px; - line-height: 22px; -} diff --git a/docs/docs/gettingstarted/basicconcepts.md b/docs/docs/gettingstarted/basicconcepts.md deleted file mode 100644 index 6592c0423..000000000 --- a/docs/docs/gettingstarted/basicconcepts.md +++ /dev/null @@ -1,37 +0,0 @@ -# Basic Concepts - -## Definitions (aka Metadata or Blueprints) -Conductor definitions are like class definitions in OOP paradigm, or templates. You define this once, and use for each workflow execution. Definitions to Executions have 1:N relationship. - -## Tasks -Tasks are the building blocks of Workflow. There must be at least one task in a Workflow. -Tasks can be categorized into two types: - - * [System tasks](/configuration/systask.html) - executed by Conductor server. - * [Worker tasks](/configuration/workerdef.html) - executed by your own workers. - -## Workflow -A Workflow is the container of your process flow. It could include several different types of Tasks, Sub-Workflows, inputs and outputs connected to each other, to effectively achieve the desired result. The tasks are either control tasks (fork, conditional etc) or application tasks (e.g. encode a file) that are executed on a remote machine. - -[Detailed description](/configuration/workflowdef.html) - -## Task Definition -Task definitions help define Task level parameters like inputs and outputs, timeouts, retries etc. - -* All tasks need to be registered before they can be used by active workflows. -* A task can be re-used within multiple workflows. - -[Detailed description](/configuration/taskdef.html) - -## System Tasks -System tasks are executed within the JVM of the Conductor server and managed by Conductor for its execution and scalability. - -See [Systems tasks](/configuration/systask.html) for list of available Task types, and instructions for using them. - -!!! Note - Conductor provides an API to create user defined tasks that are executed in the same JVM as the engine. See [WorkflowSystemTask](https://github.com/Netflix/conductor/blob/main/core/src/main/java/com/netflix/conductor/core/execution/tasks/WorkflowSystemTask.java) interface for details. - -## Worker Tasks -Worker tasks are implemented by your application(s) and run in a separate environment from Conductor. The worker tasks can be implemented in any language. These tasks talk to Conductor server via REST/gRPC to poll for tasks and update its status after execution. - -Worker tasks are identified by task type __SIMPLE__ in the blueprint. diff --git a/docs/docs/gettingstarted/client.md b/docs/docs/gettingstarted/client.md deleted file mode 100644 index 04be2da1b..000000000 --- a/docs/docs/gettingstarted/client.md +++ /dev/null @@ -1,21 +0,0 @@ -# Using the Client -Conductor tasks that are executed by remote workers communicate over HTTP endpoints/gRPC to poll for the task and update the status of the execution. - -## Client APIs -Conductor provides the following java clients to interact with the various APIs - -| Client | Usage | -|-----------------|---------------------------------------------------------------------------| -| Metadata Client | Register / Update workflow and task definitions | -| Workflow Client | Start a new workflow / Get execution status of a workflow | -| Task Client | Poll for task / Update task result after execution / Get status of a task | - -## SDKs - -* [Clojure](/how-tos/clojure-sdk.html) -* [C#](/how-tos/csharp-sdk.html) -* [Go](/how-tos/go-sdk.html) -* [Java](/how-tos/java-sdk.html) -* [Python](/how-tos/python-sdk.html) - -The non-Java Conductor SDKs are hosted on a separate GitHub repository: [github.com/conductor-sdk](https://github.com/conductor-sdk). Contributions from the community are encouraged! diff --git a/docs/docs/gettingstarted/steps.md b/docs/docs/gettingstarted/steps.md deleted file mode 100644 index 695aa6277..000000000 --- a/docs/docs/gettingstarted/steps.md +++ /dev/null @@ -1,36 +0,0 @@ - -# High Level Steps -Steps required for a new workflow to be registered and get executed - -1. Define task definitions used by the workflow. -2. Create the workflow definition -3. Create task worker(s) that polls for scheduled tasks at regular interval - -### Trigger Workflow Execution - -``` -POST /workflow/{name} -{ - ... //json payload as workflow input -} -``` - -### Polling for a task - -``` -GET /tasks/poll/batch/{taskType} -``` - -### Update task status - -``` -POST /tasks -{ - "outputData": { - "encodeResult":"success", - "location": "http://cdn.example.com/file/location.png" - //any task specific output - }, - "status": "COMPLETED" -} -``` diff --git a/docs/docs/googleba55068fa3e0e553.html b/docs/docs/googleba55068fa3e0e553.html deleted file mode 100644 index 0344c087e..000000000 --- a/docs/docs/googleba55068fa3e0e553.html +++ /dev/null @@ -1 +0,0 @@ -google-site-verification: googleba55068fa3e0e553.html \ No newline at end of file diff --git a/docs/docs/how-tos/Tasks/dynamic-vs-switch-tasks.md b/docs/docs/how-tos/Tasks/dynamic-vs-switch-tasks.md deleted file mode 100644 index e81327d49..000000000 --- a/docs/docs/how-tos/Tasks/dynamic-vs-switch-tasks.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Dynamic vs Switch Tasks - -Learn more about - -1. [Dynamic Tasks](/reference-docs/dynamic-task.html) -2. [Switch Tasks](/reference-docs/switch-task.html) - -Dynamic Tasks are useful in situations when need to run a task of which the task type is determined at runtime instead -of during the configuration. It is similar to the [SWITCH](/reference-docs/switch-task.html) use case but with `DYNAMIC` -we won't need to preconfigure all case options in the workflow definition itself. Instead, we can mark the task -as `DYNAMIC` and determine which underlying task does it run during the workflow execution itself. - -1. Use DYNAMIC task as a replacement for SWITCH if you have too many case options -2. DYNAMIC task is an option when you want to programmatically determine the next task to run instead of using expressions -3. DYNAMIC task simplifies the workflow execution UI view which will now only show the selected task -4. SWITCH task visualization is helpful as a documentation - showing you all options that the workflow could have - taken -5. SWITCH task comes with a default task option which can be useful in some use cases diff --git a/docs/docs/how-tos/Test/testing-workflows.md b/docs/docs/how-tos/Test/testing-workflows.md deleted file mode 100644 index 1afb835f1..000000000 --- a/docs/docs/how-tos/Test/testing-workflows.md +++ /dev/null @@ -1,34 +0,0 @@ -# Conductor Workflow Testing Guide - -## Unit and Regression testing workflows - -### Unit Tests -Conductor workflows can be unit tested using `POST /workflow/test` endpoint. -The approach is similar to how you unit test using Mock objects in Java or similar languages. - -#### Why Unit Test Workflows? -Unit tests allows you to test for the correctness of the workflow definition ensuring: -1. Given a specific input workflow reaches the terminal state in COMPLETED or FAILED state -2. Given a specific input, the workflow executes specific set of tasks. This is useful for testing branching and dynamic forks -3. Task inputs are wired correctly - e.g. if a task receives its input from the output of another task, this can be verified using the unit test. - -### Unit Testing Workflows -Conductor SDKs provides the following method that allows testing a workflow definition against mock inputs: -```java - public abstract Workflow testWorkflow(WorkflowTestRequest testRequest); -``` -The actual workflow is executed on a real Conductor server ensuring you are testing the behavior that will match the ACTUAL executon of the server. - -### Setting up Conductor server for testing -Tests can be run against a remote server (useful when running integration tests) or local containerized instance. Recommended approach is to use `testcontainers`. - -### Examples - -#### Unit Test -* [LoanWorkflowTest.java](/client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java) -* Testing workflows that contain sub-workflows : [SubWorkflowTest.java](/client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java) - -#### Regression Test -Workflows can be regression tested with golden inputs and outputs. This approach is useful when modifying workflows that are running in production to ensure the behavior remains correct. - -See [RegressionTest.java](/client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java) for an example, which uses previously captured workflow execution as golden input/output to verify the workflow execution. \ No newline at end of file diff --git a/docs/docs/img/logo.png b/docs/docs/img/logo.png deleted file mode 100644 index 132d52cde44fe3585f280cdb69f7eb6d2ebc4249..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22312 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 z 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 fCY0qJYI;`nP$+dZ9n~Ube8~R;VRQyx diff --git a/docs/docs/img/task_states.svg b/docs/docs/img/task_states.svg deleted file mode 100644 index 3fd55079b..000000000 --- a/docs/docs/img/task_states.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/docs/img/tutorial/SubWorkflow.png b/docs/docs/img/tutorial/SubWorkflow.png deleted file mode 100644 index f91865abdfefb1d68321faeb74d9cc42751d5525..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8035 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 E0K~@dr~m)} diff --git a/docs/docs/img/tutorial/Sub_Workflow_Run.png b/docs/docs/img/tutorial/Sub_Workflow_Run.png deleted file mode 100644 index b1635f2b46bbdf795ff2277e86b91a54fe1c9a4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87111 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 diff --git a/docs/docs/img/tutorial/Switch_Fedex.png b/docs/docs/img/tutorial/Switch_Fedex.png deleted file mode 100644 index 63cfc50c36cb84a25b9a24e19851e566c34cd4c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44581 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 diff --git a/docs/docs/img/tutorial/Switch_Workflow.png b/docs/docs/img/tutorial/Switch_Workflow.png deleted file mode 100644 index dbac1b051fb29d9ae07afae0fb3017bd22456155..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21721 zcmdqJcTiMY_bu8;k|>HKNk$NmAdR3v6GTA;1Vu$al7t4y8Je6`k^+)#l7NyV=O9^P zlO$)9AX#$0v(M-Ge((PGs$SKtSJyh!QF`y*Yp*reoMVnT=6a+2P>zcH9617kpt^tW z&SM1P#9jQ)$rJFK7c6HZ;C}=*kL7M7vO8Gj;U7e3X+>!SA~%HMK$jT)O=fvd%?5!u zV~hVG7&BydL?E7+-@haM#8GQ$lUJ;WXmsK7OjNW3>O^{Ue=Bt zH%qseIGZ?*we4KX%hTt~%VL(x_SP|f6y|;}|NfnxJzkz+_V#o&jJ{KHgA#!VM4$)} zh)Ys9#3JTk$I8jc>E+9pQc@n()$X>o8#C>(+lzx?v9TJ;%HNJ+KYVCNt*@`oF&Q-L z%huFTS0C!>k=Z6i5GSG$!YsF}^(Sk6ZD!h+5Xn!kH35_Dt`NhRgrGy@w@U545 zdE3XwkLS9Q&t7r3%Flo52&yQz9I`)hV{ofp#Im9t8?M738J z6oj*#LWskgSrnU(mVMw+-P+htb`hVR?@1#gBeSxyT0~=X5SBa}lokC;!^MOrPLPw6 z@BF-)P*O55K3<)h`yQ5cgcL={|5QU`VRlwkTU+VNqn6Eu{=wvnQ;N^~^z`Y|E{AO?7qkgM))!Q9;_@%gcspm2QWkyi5qnQy7x5E9cKABqn->CKbhsSmb@$+}`e~ zCHN))kGp&3%o%bD3NDTzD1186D?WGG3(YG6OOpJr?FUjTQ=YOHYyo-;I4-1o#KZUSP zK^RxV&VJyOa9cXNe^z;u&`;}Ghc8$`qsjId0V1<)R)Tk zeUsA9-ZfR9pZ#5@2Z_aOEa$+4civv|pn#dU_RM!`>ScPpa{YC^#VylLUfvH$ShbSA ze3i6^28M>+;4ab<(K^rfx0ZN#c#C+#u zPV=`A9zFPPwRd^;`0?!zjG=QXcdC?Fm~SMlIGlXr9C3|1as>PI$rF8j{my^C+Sk_? zw)zt?^lKTqP#kMIRr>p#^KE@=Ozv_Nr6@fr3CjrtiWFga4kprWZ)1LA!#3TAa>ji_ zkk#pOzmAmQ=Tc*cHhu4fZSPu;YqeOg-g5n+JZ!-v$oREQG% z_otVXI4+Nr!puKmOxf7jxb{5fyrg7&1R-LC2%cfLwWua3DY>QnAZYf-j~}^m;;wr_ zyab36@Nil|tnpxeDJdzBEXBEyNaW3%jMXAk6Bp)~5ZfXKCr+Gr_3G7U@#RMMFM^jY zdH?m~69q*@+0S!dHiur>+}wO}!J!1R`8&^`H7Pk6;?UfC#5ZIo5|QEO=NAD}ZU}R?Z$FQRhZS3lYe77F^vLSyz{$S1;i%P3!ewVQm{lx- zS7T;ng`I^ZE+XO?1dP9N@nNaMik6}x)q1f3{|;TL%vJH9Nbpn4aK? zKbGKoUsu=U^t8=FA8#2Y;+zNk#t%ziM1vtY&x|2Ske*}mFohoy2#I;{bI#}gmJ4p0 z=;)l6kZ@~_;C=Myk>ugL()85Sv+Nh8ZijnvA#7c|2q_f2I3<7e!Jp#l!=)9MS+T|H zgHw_Rlc(avoo=fVdSGDTnZU1X1zEW0`3&|~Z55u9*zw3FL_CG55?mZA3}Aits9iR2 zr`UbJ80EaxX@^MQ-k_B78ynNIf4$Xi;TF~q$Qq{@BlIOdKkV;)e_*E_H@P2>E)1S> zTP0+PBQS!favnF`6q2S8{ij==zjD1p3^sre9irC{>lu@+Ras^>Ty!^p-gmAgkO?`4 zS>WB$F3Y3^-=tA&p7r%>THz_3YjaGWIV2<`goPuhzE8ups9x=c zGCPHkk-|wCD`CZTNlAq=y)Y0{S6fMRF3&MtT^h*MBc*0ZxK7ur0^j%i`8^F{spC$h z@(}n${ud=udfcC9hYC%;eECxF6tQs%MVMlsum7UL@qObHWhFg5y$27-&FBz^GVp-8 zXjuru0`RR=sGgG$PD$YEMD#gNtY-}jS!hGy5;#7_4a#sotT7RWgv1-`+1-f@0z{+( zPdtZ7Jq@9P5<~Jp1}6mW$Xoh<`_gtQ)B+FHHW~j?viwWJ6kQ34FN(I!cg>^U@CI>7 z*hjvoEuakKq^|=f^Y>09%H+YOPP60RZ(X;<1$t7vavVfx{-ONu@E!tE)W;7$&$-c6 z7TxGhB!@D|dvSqr&-Y3~JZVeqyuAchU&w07U|v8+htEFZ9m6y3#Xy~z$&?@&-w3uR ztH|va_Z}o0m+6qp&?*M#>SAxgO8)a6Vg1Oi#OEq+3(+H=`A&NO67D{GJwiD;=5j^k zM3ON!)KATA#$1kUat|Gy$YnTlOZl9^W}CX)TBZ-hx2PG@-C3coq%!Ki@AC)4LFwUZ zEXBZ*d~f~IgZFb9bT1g^f|JOtOOli|S=#SdB5Ot*llF!#E=8|4)|QenNbS7VUp>q4 zcTp5)W`k1W?orEq8hkg?)Q1zV39m^hcxWX=Ya(CLK!cOE$N4xE4T-^RT?_DOjue3szsywPMu2qbF3W;NcaFQ#j^Qr?P6ax1=VE zcmwgjQ?S2+j?gLTL&`FenUIvk-Qyo>d9#WR&bgArAJr-SY2R7ahTfgMK2QAx?391( zWOtnOdVYc2*qR#cK7VeT=cLWWsOkioEyM5cS(u|k8>g#XR^DK*yIgakX0OGg3%tqL z?5@l1lBh~c#n3u1=AqRAw*K>FgxhV_bB%}RQ%XAkH{p%5{T`SF-IKSmiOff8^4I^~ zk9>@~+@ve|mI;jVpBsstNrL~*T>Bx)W`mbWUfTPH=>Gi{-@)%<##%*4b zzn1)BK6-k*yhS2$S&^ z4tDpeWs*Up6L}%6diLLOm!hVtWUlUfR#CmMaj18-G!cuB?QNBk|rp^X2h%m%nXh?f7Hz1_L*g}`gcPJIyY|C35sgC z=|Y3meZ4h^kuU?stw%ic8QWGk)tQGkA;(u(CbMEaf_nM^k{fHKh`?&g$tKYLM z2`;zF%JZF!+G*VSJCllrxaR_yP3#BKl$tpLK`Ztowl%o!*f-V}v(L+QKAflYZPlq{ z5^-Nk47EHGzK`J$@1-484gXxjBQ4mlrGD<;fz{1N? z%tYxGGmyMt5?3Otq55?C%&2M^B8eM0p7K@5c>#Mvql>=>Cw9xL(ZUiB!xflZ2xI@cBH_3dZ=sZ-b5jNz zf!A?0wq5ER+3a5KHG-N#lGz`bVooM~*EIO({d3aP`U|=!Vq~nx;oo5kI&++xD~)fj zpDyodLI=4R-b|8?U-9SC6R$yI@Q)Pzj?|PNxbrB?^z?|7ERON*6{SBkBdUBU?DvNL zXPs$77VFNpW24I1?tZ6TX9?{&_4cm`4x*S{Y^TmOI2+#jDR?Lo<8ye0iJobJ{J%ye z%l`G&I#bLgwiPCmH+Iyieoj}_?X8Fl-F9vf{kwa+k;SVAYtgIm+uUyF!{Xf>Ab8zY2*8pr~Zlar10iEm^==*-LvEF$%|fDe1#q^D$M zW$hgtdUnVkIQ{u~UqL|uGF~$>L^8y<8-<01iHV7)PoHLDV%o1+7epc>uc@73VR4yh zV;Ukskie|Bh>D5f0Q{?~zJ2@F#l^+i#%4b6VNT#^wR?P@n8#!Sz^GjA9PAAdksbp? zCj!8LF#LD}k{E;^D!_>lE)YVnwch~I?4{$qc)AO$o%8(YEuNx_~(Z!hG0z}L_-3?Aof6BUVdR|DMf?O<6I}w<3ewLKfgxN;_77G z69J940Rcc~yfQJFmp~-k+@SpSeq`hYeLAyUGcqX1$hf$; z5}gPTgI%0^M|*ovP*A1yFLD$c!Vv+a*D{bP6byV7w)1B;n~;E{&z(N~ zNLg7bgdDL9E^Paig~fP(Kp{+b5jgdK&s(@KP>g9kWFDm6Zb z=b1gibtTJLwa6hvfSC&T`Rf<2THepS&4uu@g==XpZOzSzzX&~a0nGA@RJl4I?rnk( zf~>>>NV(Oih5%p z1+z9cH=|IfaO$inrQN8|P%jmU6D0ts>qOm;4&duX?F3~FSR~!V#la5( zZs1pIWF)_KwlBQ?G_0|FBwtoWM)K?_#0mKHL*GMq1K>y?We1<%t&Tt-Hz+sc@?jMl z8MIwIVLB*bbvj@QT*fM#&>eA!U85S{@H*lp@hop2g-1W2dJzl;vtf;Yy3-$hNF>3; zfk;WPBbsRFM0S!M=?4P2Ar#@w^lF0dem_1PrUVxaS*XHkd<+Nq=@z2z$>Su>i1^1zwU&dTwqKK}{3Xz$M|I zMz%pIc@{CJ2;3?6;C)FaIqJg z4g0A`c&))WPuM5q=r^B%`*^{GH42PQi(N}yBG5UBM*-hF30ouio0=Ja*x_v_JOmdn znMOsJU;`gESqnBo9v|xW^aEA6-|o&Q;jSxbmJVe6%n923h<-^OADbMzPzw*IcJ#DK8t)ZxDSJ|C!`au1?x%!}+D ztI%z;@$T4&h$5@$#=gF1Lxu=ZK#b`5IryGoq?Uof6)=M`Fsn3i=Pgq}39#I6;{v-j znlC`wxi7DhzUqCadG@U$SL*P|smm4RA?GC*Bj}ULE!Ssnz51{;sJ_zr@`q83{!6{G z9JFi7Uhj_BusA($K)53o&n%bS+wqx>n)KJ;l@7KYM0Aqo*#3Q>fsGdQwWy)lX8DJXo90 zJGAUY_UI-B`L2Zng}AzEITV2qgwsaYTH*`2$F3gq$UqB$6_pe7S5xvr>dro&Kb`aCb~Hm+n^z@#9v3GGvBTIHF7 zh9plDN;Pz}v~|=SM*9SF* z-QQ?2Oey6L`{SO0fq{|H+!vKJBN{u-p63=E;vr48v+qbyL+c6&#L=3X8hpWMi6}t>c1K!erR(}kJCHE2 z+{s;Md)F7Vk1_uVb9mgrOOyb)`PHTW36)eCBsJ{fZ@6}-oJ;>FX8wiUH9^cG&%SC^ zx*s3mxiXa~9~Tc>rd>obv&4oTL|@|1S(E*dhodMiG7hdU*vHrs>9@%F0TiWv_A|Xw7cm zi+G#VIyb@Z)#mCoK_$@vNMUe8uE76;#XNZMz(|vj@hpZ!y{o%B6hZ)?u=4T4VA+?9 zSjZIg(de&g`7>}|K?Fe{1&YwD8DPWPw`Eq-Bvj24=`%Ak2CWg3`hgQDaPlnRY5Y&I zr+PB|tZQbhp#V>QH$p|i-qyDBBvl7Q_o#@7zTshc6WVWL;9E4I?x2;UBYpQSG1U)& z@R*nwC{EFGKI}Y4w8{Y^R(E#Z2S%~TVq8LCVl}*4>lCLR5#%1EJ1P=@?0P1sUaEf8 z3JMIYhljPo!%hKS4G9PkgR;rP-K*~-BN>>PhXJOWkbl#Fk2%4Eu8fq*=#@H{n3x2F zgp31@f&Yw%2^_C)P}0hLTrpVa z%L2l&Ko$W2{E(ZUKVHm%PwYjq?wDBbj~~21!6T7jeJ2o6@I+N8?^skH+C$+LNWRL+ z@DOdc-765nySpE=v$Na3hQO_~Z~_5ymkh{@h?qF{I!aLR4}_#K%a$gXT?pOmH!VH1 z388Qa`BoT*BKNgxKhqF^*D&3KgVFEaeS5Ig%o%^u{||)qhWdKO&G3+hh6eeFYr1r4 zXcPO@i5l1Wde3<7k5$f9_|d2hoQSaF~pgKS&mwuHh$Ls{Yi z-%Gl;L|xZALrcpp;c`!fpaO7)sIr>c?jSlow=R#4=h=%&%!kyWm!DK5MB$BwhScp5 zBD%Fchx^+-ml1f*Aa%6Vh7%~(52hV)w;)I*f1!q~asDtor21&{Yfn#)2oy@O5-1+d zv-I?Cf0{Xuj*j3pg~+}^T{l6q+0!ubw}kR@TPEOYLkkDR=GW|pr9ZW zJFst94lHJZ*d?)&0pyCrtJGjPkDQ!#H|Bfff>}zzok1oS!yWc391@;s_AhiXvk>96 zO09j{_1mJZFH#XGo>&DOWYh{H;VU8^*n1r=*14uUTemT;oM-S`ZW*Y$)0D z1c$HElaqIMaIr&p_>Oh9vbxS2&k42Tyrw^!n+@@YM9H@yiys{wUFiRczOuf)&I2jQ z7q-5UkqVeIsCpf*93SyNy-Nia`>V=ze`Ql&zom(@!F3bNvr&*i~nOaT4wk0Ly>B) zk=f#!cGUswU>Gulp4AdS;JMx&ur!>UoW(^&-A{NY0V_h5F^h{=aUYo#soqrt$OkD) zq05PMKR=Q_WKqz-R_wMfQVuC+$ z=u9^0;o%`9S}Rri%YfA^ADr%F*q|H9?>SD{avJ25;WH!N=1tjoo0=KmkC_ zX+Z4P^oskj^HWH91n4(kzkUtyG}nPwh)fD9zm{E){|sEkJ($VHKNz9~WbOA)BTbBr z-vk8#Yb80_F$WqI!;UySQ!y2OF#%K(C0?w-fvjR%$srQmtrpKp8z3bCM$I?P6|S(Ud|-qpNP*%l$5kVE(7>7 zG%Rd$hk|UmyR$O~@RzEVlL6#IFjC+>ZZ3c^=H})?32RZ)$<-C5xcPvNjt+9`#+`Gs z0v{qG_I7s<|3p^TbKRA)gX?`+!*fDiw+7LSMUKYC#-=Q36k}P{)$x_$V{ZFPIXOAN zaCAOFG$f)0e87xdcP9NI5Vw8*?zp$XrMQU!JOsiTTLOi&vuYr6aqbBV5BE`d>%rZL z%*n{jy?*hcSCI1Wqe(@xp*#b5Wn~&oL_XN`#KeTdm=kH*S%P1X_157TkjIbz*|H&D z4Tb>>SLoE7C_}u27mJ0o5$ptD3Aj|qkADbKR$$p)@m6pDoUA{@F)_4o~bS4Bg)x+YjMz` zV$Dw^ezx`oq{AAyFNrk~vqC~bcjV+kSZ)d4xbcC507~&QDd5QlEf@9n>SFOZGsr0>pK#d_yWtMD&iz zTZOor*M)?72i)~Z!+o$$k>IElfw!yN9vd!qSXrON8FPPUU}M8ZMcrfgb_MqNB4eav z%1Nyt0hhVxVk2{NL;tg;i_eSAKR!BMO${CJBxd%VKS3!2*)A-q*t7NYY7`OH1MB3A zH|9{@7bUA8xey}ro-6@2O!34!G8D2TG&Iy<+|^;Iz}Rtp zK??{i#ryXYDJG~P^N0B3{_BG(TnG;2@nnFFFc>q(vox^(IJhaoP+qvENr^Ew)Q4CxXNW$P?5Me29`QovwZGEiH4io z5d>u+y~e=wbnX};fwLQJM!-Zj1u-|tY8+UH$hjV@HG#jhB@j=@$S?tP_qA$|`ytnf zhqrNo$=TViAi;aDC7k1?&a;h<}dO= z2v~NAnq9=J6;)MLpywza!^mTw8nm;J~E}@c@Em z>aXyN@$N^r6p00-aklegmCktl=WhVa7Kl%T=+16*h^+v0>LykO5llmc)B2dpx?)8N z;=+c4JK*b%v3S`#cY5*hTzN#pqS9_-?ok=y)j+WYyV!%R?d@uy1Qk!L_+jVo8ynB> zuT)Fg%qXxheKtSXS=%x9`VE#B2qw+PmkktVs#o!C+(sgJh0B-qM@p;~Cn)!`=qt8{ zOmf0i(*R>MKI3?0Ltz0FY+xQxA9$&!2h&if2e1sBnJs}3h*{jhWFTyV zNF(+Bgh3}#QLlWC*`ne!Bcme-C1Z$&1YY%CjeC3F1V02;G5h)M6wu%Z(N&OOQXj$2 z*x257-yU_y&&w-WxiWqpyG;`6U8=ITp$4qK{$V~mq@YslW`Zlg0sjLhG z@fP-WdwYAz{Sn4(hMuNp?qe!f*j7p|A&$7oLv{)IdX8VZ^Opww^1cHF(!@T|KQYdR zuF6~0k}IWVWlXQ}ip0%K&^Yo@3=Ghu+R3Z<@Ql}N(U}qt*{5T7$q3-0q7N8A*s$tJx zb#YXam|OLf`;58a*w;*}sV9v?CGDLkdcan`#!nw zXR_jEq9!NP@_Xip4$wz#ZEjMXJelME;B*rzj4f=9`Gm%uOP$CY#3UpgF*hjOSrTcO>)Yi~JrPnO$0POcmLw zjb8#Ay>R0D?`UHK^1Y4y4+l8vqOX?B*PpsixffOK(wM%m3_8R{k10iouy5yoM)xUl zVfYzM#zsbpWN=Tsv0s`W;TYYHC*?%VC3C5DU6SV9BXS=L1$JkDKiZb=Tk*BHoV9Hr zQZ#mVL;z#77+usn&r~=2_BdYW!_at@S4Nv2k4OVXdoqfG&$1Iaxpjt>$|iO~=|gRn zVNBcets9Gizi;Duh3?>_GW;O5!`5L3YqkUVJm?eAuWsXP$x%IG8*T4kc@xp1zSwzI zj3M~%m`>zJa9kZ9u%0vjFRr6TAuq#R%|$;+iqVxF`-km3xec< zQwjiWM^D&Nqf~0dRw~w)EZq0M9`AiUZt*!>4LRNkL83c8Wk}Z@?N=Yij=!P@N?K5p zG)ClA?`I3w1&ss0Y4@FN;Qc`zR(9^6Z|Td@INsDgw(~&@w*w5Dd)1kvB(dv5<4TT$vG)vCo`(v&A)q&o~ z(P#%%S(S5jJs8vO)?6YxRena}{b0Gq2BUU?#zUGfMDV$~k8;!qmvOq5yFT!Qa5zFYow zDFPDo#eoeF3JWb zS>@Czqr|MHh?sefSN$bBDF{a126>ncy!2+sGLHmsnUoPGk5 zWn+G|GG47@LxkIzWOgg=q}FFyQ7}Cl~@XTncLH)Ej)`fV~F@gxV1g%fOh7DdhHlR+1BX0%*<={j&e2xI6VvUOP4MgFV&rIoL2*RhMCRkCoC2V zDSa2*1}n-g{Hh1Ykr!gE%^+EYj1TU*o8uYWCqW773UT0hvp26vN*+T_FT~Ip3fUE0 z6Im3-l8FXfEySQqgqZ=J@J|A|(HlE|^8e&NI6Co#q?h|oCh?`2GTE}9Y|F7|mGlrL zZr+krG#9B-r*Zn0X@XE2XN9Q;-y2KSLDwn1b7SJAh3!6(TU|uxn?o*a$%eR+%5P)b z-DAQ(5F2d-8wv=N%?ZrRS-HA`OLNf(j3FdM2n@$W@T3;qT=YyyS$Y@Jk`iTuK+#I! z;ykdoh*36YFdPs-J^HuWHgXBCuRJ_p)Zb%#hdvZgF6r-5{!xDvGHdC>Do$kM@N?$c zSXilNTicsc7qUV+{HkkE%vbXz$QI^4f5lqKo5)S2Gk=5tkuhRWf+(4&ZO0yVwy2g+z&MX z{x~xXOQuTgT(xo5$cQF*u@=pZ>GOK3ug1-d7Msi~87jw#MePDQj1NcFxhfp;x4j&fUR@Ug5%u?90pz^unYy zT5bFuyIa9p7DfIsP?u*}?ZCcihZ5 zAC}-OB`LnrEf3VjYo;jUO!?D|Cujm+dOf``lG+_hRrug;Kl!uNkNt1vtd?kaC1b0N zWHi`nCsPZY`4@1@0||jJHooc)LYKRjt`4ZxYgoRX<#UJ~8twBz#Tm(y&EiH?4|=cl zQ{T{lE&$Ss&2?FGbWWB3#ctG{)zA-HZELJ41Iw2Oc-xD^H`)rC)L+|cUSAk#^SR_} z+e^bYtlTnK_awU?NiaN=M=N1h~(e6+g57!AmcsfApJ#CZ_&l*ohmP`5gt#b zWjC}ZtniO<(n+ng+Q=B&5{3$WUoqSSHzoF>?idr%ev}b5)w4UVmCS*TnT5)0D*jfj z0kie3j|q$0(nclMCAH_jO=e@Gi+qjSql&}dOlXZzW%4{*EwMkTT4*rieRC*$YsH|J zmpM5NyH;|eE=qgJtMXfYhkf{1Ti;o-R`P;;#of_yx3Ky}w}So>fs$Um7#2+JmEps$ylm*D``y;+ zdnDKJVXcZvt*>)}9X9FElX;p2*IB@?W{z7L2&};v?p7g=iHs}nlTwSRT1is$CC*RL z=*1iV`jqlG>v9&TN$D0ozmtx#;$wkCW-)1Z{4g^U z?Md@tQpGMQwJo&k8v_5>A(KeL@Nq=U_D0(p=ACi@I@q6?b+fH2Np!**_mek06oqs} zjvX~IYYiZy#U#MZGiHGL!BBH2egTWora}(lj~!KT`hk~PHUpXWS@_OiX5OtQe|m|U zrf*)8`1Ju5wmCzHviWA>+^8j0oA{3TW}A_yyoJhm) z3Pcn+5#~MLj`oPx?#OVGI}0O3gmE_qI+h>3g>I}ZtA(b(_=h|_Gcm=d?S^BmY1Urp zUdunzp;FMnikA5&peUTJW?M9W5F7*nglfFZ0FGCe)W$y8kmFi+zJC!-%rq8EImALdh0H;X32hM-Jnw?S%lhZr?-}!eIlCL z(F9k|FE<&`Z2jzl8yZ=isrdDFE5*YBwsYuq2F3EE!;jK=CS-i%vs??f#Avpzyy4;J zrVE85T4*QRHNMMg0_EYqnTo}WngqA5?KB9Ztk(Q;Mr3zvjjp`LmKeQ0O>2I#5%Z|v zt>5}==N%KRl7V31ltbr1wcGZmmdRU13zQPER=AF`GbNK_d8gDLS~Q{_cfFt@n-RX=lQ&JrZ|dpN+$P7!>sm>F4z=5)_?0U_k7P9M z{f-Pg;p6;^Ok20_k=`mULepz{Cxxr*S&10gESF&Y=ga42Zq}weO;*q?)a+ST7#TI(O zahDiI*@>iGZqRo<)eyF9QOgil8Sf-hAv72eYk#5Z<-KKtAwAwZ<=H2T{a-^^M0OuG zN#i~()KlAG8bk0wh-F?!f8~w5s#g}*MTn9wDCa9n=!_;^a3%363i-cgEF4sZDkI4% z)j!D~HExqC_1yk|9n>;x);hoFV`(EVQkli|vhQilX!ik4x3kqL^rX>```7@>TddV% zMqnwrZknL8|bGQH7EW${9-L ziK3>hOk{I@*3vbcuGsO7)hL@cQ+zKVSvF_KSHe)eAKQPd{FP?j=JfI>?-ibpwUQBY z(bn=8U8eBXnt(pj*Pqgg3LCQ$a6`YV@XA_0z04f=Gl{4;<28G5d8N~AZsDcqZJB0k z>dFC*^sPWWSMu%)3Oo8sho*u*2he2{8naVu&u1^a2!gw)Y8l>)%FW`ZP4tDK+bvSw z#sp4t{}|Nt$q1D)5i+fV+8Y8!W|Wm=b98q#BH%VPRhcZ&W^3#9+&248S2GK-8Wp`y zN+W#Ws$AH8at}q`ewr>{CFVR-TeS+CPueti#q8YRpZ3WwKx?DV>FpM&`E?eY^1SU5VH2V9Kl<3 zRWKCaHk$D|vZ}w7>YDDtUcVJDLz{I~cJ-`JY^?U|l;U~EsiIsugJTPtxAvqx+;-wJ zlj*`{=N8h7?B28WF2mK!QNjP&^*;N4yPHk<6}9zsm5xfx2l>XAr_^R9JdFKMR~yiF zgxRYGtGH7uZp?Xg)ok?ol&4r0pO%zHH%X*mIJCrTXbYCNl7hYuI8PnmV)kr|Ml!GY zFn>Qv)i)h&wRK+bqtyz}W-mYcJ9_7ap{8f;jq{q1yvk3;Qy=G#NctNNW!))0n{}~= zUgXgcF7dt`^FDq97ow+fjh}yVPLQ~DcL{q=H^bGI+a%H5doF(iqg_($TDx-xB+EBA%@iosOR8$SjbCX#$iX{tbwD0-Gady zWYxi+Ru2z^`t%=Q8^$Y~!e)qvI){eNK_O4$p2X2Ke++{oBl8rru-1KbVr+vagQf#2 zwRbhx#Lo*07gbcmJu7xGRn@N>|Au%-l}T3(vTV&jpHY-T9i8G1pa-ZZ9_ zbH1y{>Zz_SzO=;5?7#2e`i)IEAy+qu%DkYlhB|pO@CztyDJb;B7@djhO@<%PmL)tj zB^&(Q6x;?SCfN)}M^L^Bn+?^c)6>$*ge1zC78)HB8{$CbKCm|q;!iZ1)9k~f@vB#$ zF4y}S2GW^MJ)>A-T+d#1Q%*l4bIX87qu&knew}y%=SF9kJ5XN?KJtwK8Mz6(i>-79 zzRUSznTDJkDavo`MVW1LQ&U=5Sq$GoJE(1pnM*V@G?-C-MY-(`_`*$st|^V43{%z% z#<#l8sT7NcrvJ1E9O_&#B%I39D4r9XqZ)RZ?f95X0n)O6dC&h__{`@x4kaw~Y1=|; za10T~1IX54OT2iv#PY&FJZ}qVBP9+uREiubkB|1D-Unw9B=Sgs)ZP`kghA`I5B0dg zXUr&!?d{vOpwpcb6`;s^8_q4V;gmDL(p)gar>OsoZT)BXX~o-MHvl%@sA9n1p-gaIKTp)KoYAg&Xjxf*J(?NQgg&0;`q0j(ri z|Mbg3acce>NWE%nYX`JM4cnqVfBp;L^V`o&9^2HnJffCXJdo&PDl{-?y4)H0xzvbC$nfJ8NW4b4J8yTPCM zY(fhl&d&@-NAhX5Z0Q@qnIom8rBG`5wD4`c6ImE1;fewKrR7l2S#XD*6%fMN5+i{1 zhQ0uhCX3j$qruRjAGXB(SVEJO%^S;w7m$F~S{p>(;B*FGouZ@^xBhujlk~g?mZ=_i zMv(g`Xs$<#IY9Hc^V&x~oHXw2DimX(dI+Kl{8<%+?PJ*=A|re35Jd@SSsSRGL61Mk zbn-8J8y}ZMBJ<&>f^0$&X`FZ({K?7b{IgOIjluY8BddqZT(s^e^f!QV{y`iM zci0 zh2-NA5m$M5_VLXt7-8$5_gLbn@G=XKgH-JO&RD#%0op|9nYM+VH9G_rv{GhJ6yHsh z@t4^BNh{0^;t=!QKfl1n(iSR)K}|N>7QKE5dLWSQyadtQS5-ANCI*J>{P@GbN}ZXG zkFg&=2IU)i*Mh1@U0RyJIk~;@3+zok&9VaMd-xHYjV!CGk`R8J25lk!{`(-o+TZ5~ z0qC`Brcf+dtOmiRDS2#_)p`abV&GW@GOA_wSeFf*ChI&4AG~x3=odlY{2)I`mOtRwoFUzbwteK|)`? z+?SWv^XC8!^Q~LANRE0)GuIYTR21UJ^k`^@09_&2HFV}%-i0?~E*!MP&zikaE%gMY z%?oQVJ=h)|9v%=x9Bgfszy5UGS)Bw1e~<;(?ZAzUh0IP6et`PQw7hz)kr|E#STvC8 zColq1xy~Bk1e~fN{OvlZN+fOP-nkvG9mBrNc$W3jpB3LEB1DDrrCbcl26 zKP5o_h33%*lis)|S_8ZAI5h(b>L40`Lmf0p6Yhf!i-~zNDJf|u2NnT3aX@L>Wfx-$ zM>+JxLOfM*7xymo+_uw% zhPjU(d!v29;`F-AjEoW%9UtrIU6PLxb~gy(#j7zn9>#qa?t>DA2n7E=G8hIeG#!k9 zdIye9X!Z{Z`T$a^bG!09P@ysK55?DsCazu-7PbeOPXW=Za_e6Y9zX6j`C8d1+zxwK zMMXtz0H&i7T9t2^ZUlye;B_$rbGOWfgzTj(ryHrt9$JC(Q@D38;rmq{=!JvWRlxEJ zWHwXK^R-KL9o@mw*AM;(tiKGl5c%^)knc7HGSxNqaH*t%O{z^wGK5A)gDO_ECEy%j zmIBv(d-ZcEaUfyl;PA^@k3U>QU$}5VO-&7SBQPgNAWuyNxoNKV2Z%)jK+@U26FPNBat(iW-iKvN*J$-yZt@H&L>Ik(#t&8ZOejr!$3p19N-PRg>eLB`Ym$mN8iqGlUy{6G=#&p}jSS(V_3 z^Xl-bK?(PxxNH)9E054`uydG9P+0s55rFlASH9n0%6;orDX0xzCQ-r3C?W%RoS_>7&!o1DzNjKY!W+0u&}B>iwW2i;li!&~jGzc?if)AXwv@2^N0B(OZx2 z+#!O~paziEON`-H;F@?Wsv{w-8-nBy?PxnQt2!rAn-1tJCIeb3f!IWsnMaKp=<2Td3t%^*~+#r zsUe_#E#(d!w-I4sNKohqCxFl_l+})ofdPW)YQNjwe46{VgS!a44D5+@l_-c(0B{~l z6`;Wx)Y|;WN8=^j)yGhj{TZfY6gpp?0Ly*gAPrT{O0!|%xHsfd(2na0XG-aLi!o0{ z!wv_zupLCdOHhOz>aS`Hg+|WiaX85X)ZhNU6H%bYjgTn-7gN1E6YIXI>fV5D0Gp49 zjb&zMmjw0B7SW&?fPP=(BLa{)c}jy$368+Zkv@i)8Lvhb_0?@N0|Z0-Ai(;nS&qlp z^PTC?29`gawYazlk<)!WcKm02?RM2R${vAl+5_>|XGcCdgeG*tC>yrNY>K$Ai?~om z9d92WjVrnDsn|cCej`aVtpx(1I1vjridB)SH7fPJ)0YMw4FRB-FC_ppz{E%@=H;~B zF2Y58N!A5w#>aidRhy@dS5M)?>Rp-PG56y`_q{atm#F;~)vDV#-TK$CNm!2`l1xC? zWgryyrmK$@s@?vC#p9((;4mRVeuTO=h-KhBk1vcv5%1m!K+zfw>7g%z*4$~d@`aEf7u$5fc*wZUV4&&;XTqR!O(Gw~1zue?gDiB*Q*nN3hn+&V#!M zd^aFTF$5e?JwLu4*A>J3a-^A+kWk47bXXp;%Uw-Mn?tcGT46s&#Oo!ZX3l)(H zF-IT}v~p;>iYlKw z1hOvUC&0Bx&RtUkz3c;%VTr@;~L z@GE=Ulz&ZESldvL<3yqP>XEutC+XfF2jxd{@EBwd8kS3rkwO-awHURU7!lB#CU)~s zvC^bQA2x(l+kTc-7k4)|>PC}y^FZ;}MOiFiA>)!t7US*Jgoy{&KSaGR$u(RZN^K8O1bEU=!<@s`E8AiouQqhY>BJ z_`oLzDUin)EVWnV7lFS8m4^Y+2jAh; zZKUcKL6wDBrgYywV6|q%eMQ1e5={;P8QJSmiL}RJ<7Sd(fG3@74krRVzJCl4Px$i{39Lu%ILk*Y`M3sSuDgG;46Q(LGf-h! zF_CuR^07$61}z16s`O0jd3O9NZxZFSOim~+4)TN2g90)$EgI9UxfjF6A7R(3FcX4K zg33Yl;N<8C@!}c&hitRi42lI&uxdxr7lCr%DZb|ee1&C8jubFL!{{Mx6zLPd@N#c- zY|OYP2{5Fpt>?9xr?Z^byk++#!odY8Ln=bXV0s!5d`Q-E54@}Q_VLkibi>K9WE->y zcku4R3lV1zkJUd?62h_U;e`X@I@%MZ)7nNpgYU=3+s4MQmh3=E+^KyXsN?XXpP@HP z!9ad5wm1_t#8+L(iHR405;e7Qs*unw@Lk78rq5(D(KHQ24wwLdC7}>+7ifdQ0D$qR zZen7BLA8F)*Z?gS^+wcy`@dRLRE5YM#w|qra4!l_1R~NS(H(sPkc#Ds;er2P_k@Hx o{42gBlrvcO|MO>?Rj8T##j4p>Lr-Zq(24HUV{g8%>k diff --git a/docs/docs/img/tutorial/Terminate_Task.png b/docs/docs/img/tutorial/Terminate_Task.png deleted file mode 100644 index b045dd9ab311251ff84c106759e211a3f383e7ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22374 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 rE=}Az)tuNbyRG@ diff --git a/docs/docs/img/tutorial/Terminate_Task_Run.png b/docs/docs/img/tutorial/Terminate_Task_Run.png deleted file mode 100644 index 353c0db57f6dfc0e076e2289819cd1b2366a9c8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96531 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�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 BU?l(m diff --git a/docs/docs/img/tutorial/Terminate_Task_Successful.png b/docs/docs/img/tutorial/Terminate_Task_Successful.png deleted file mode 100644 index 411f531e3483171efc7621f89d7704ee7719243b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90674 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

OrKaHadZiKp)f$W9m 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 diff --git a/docs/docs/img/tutorial/conductorConsole.png b/docs/docs/img/tutorial/conductorConsole.png deleted file mode 100644 index d5ae6477a6bd54f3e4a0ad97cf4ec79bb25b9092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35278 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%0D+-b*bOD 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 Jys=}@{{v21TU!7C diff --git a/docs/docs/img/tutorial/conductorHome.png b/docs/docs/img/tutorial/conductorHome.png deleted file mode 100644 index 314b216bab22d897151542861b05eb3eab63fddc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125372 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ⅅv|BR+JpQ-59+fPn=i&L@u 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�q9U*JOO>*Wq?1Y?qyXVAWdBuag#oxboG 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 diff --git a/docs/docs/img/tutorial/conductorUIHome.png b/docs/docs/img/tutorial/conductorUIHome.png deleted file mode 100644 index c21d9ab8edee23c58f55b37ab6f550e300a135b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218119 zcmZ6ycQ~8>8~3fHXsyyJYE{viwMPi5YO7Uy*WN4kELCbnZE91ah%Hv^5i?fp5j*zY zF`o23p5uAGzx$sY><#Vq0`*nt^D$5c*dHDnj3yVlz?xQ*u7Tz!x7LF@EF6J55 zoEmd1tgl$|A0;)sjCPy9dupO4q4#&ptp_{atso!7AGP);-A@wKgI?GKJ=rJ2$HCu! zI&b)`=>=J^R7kyV)#`pG)=AwymB+8$jQ7t6{G(lnW_Q=^tjeSOB|zf$j3G}y;4}Qs zgB&|5kR$N@|Me{9*BU{}%xC}iU*p@HaRcA}&l~)E<=FNYFJ(Q>;mmd`P4WB$bp_V{ z@4*J5buWIKx=yk0MEVO4=wB@EeH;R<*szP*UeW=#9^P{ik;*@i>Z*RZ%gq zD&cvj`E@~x?R@;H8|yG4>FuMd9gN(&a%iv{zjb;jcgi0nbvFi8g9A*?Ra9T_@I$0^BXa;XYB40BLSEV#f6T{{HAN-#_A;ob-49a|c-=S&yW< z^|CFA$B-f1f5~t**4Nk7&1vkEmCJA&@ZCOt9%o`?l;Y*9=AX{zWW;4Lx3qjYC1$9u zu6{PV+Guw85SQmwoj2`4niZv7Y!;XF@|QU8+h1K}Iu_}<|MD1`uqh2AAjNhe2z3VhyvHU*s)_*q7o#|PonI8SVe^@ z<{RMWkWKXV@PKN)XRMblx@+v1n|yy}dtX(>%PJ!iZ*1Zs;Tn~d3>&)`8XqHL4Udlf zy9y=~cgVdw-(!6sar;V*&wQnnAc($RxbRV9t++@BkPrq{b9dfbE)7d@IMbltz|r7To`$X z12L!k_4$8J73bTQFj>^p@o~h|OF70L(!$_ktB{%^LD_tejSXw(@s!fphJTNwxpI(Q)j7gR;^nC^)%A;smrd{W>%3E>&gXO{=0a;t z{)Y=wE_9Hfeo5kdy>Q1&YFca>JyU1w4M8|;!+K{Ad7yu^H!fwG%ozuq3R6!?$R;03 z%Hi-I_^9dQ^XvI{9QotB1u}l~?90>L*9GK?QCW{L=b>5Cm@+fo^NC-PX~4e-#YO$k zS=h3BiX3L7lvcehelHC&GRoBAGc$6iu@}NaZffZ3X#6gJ6c!Q@UmdwS_R!S9z+F(s+AA&Bs+HeNO;>ry=!?zSfsu|JaTEV{!7GSC0(L z1{IMs*jyuJO)UH$l}eK2m$C!M0CQTd`-_v_)tsGL*N(cGiLBoxAH-F7f5RcXRO@&H z9zVZ1dptsgxx)`Z-GE@w03QGUmvR5kB^x(yU^N4RM;hCpAkayTrszYrkh6^}D@)5h zMs3fT7EsJ!ia_0BPD8%VFd`t9MtF_oK~TG1xAHG~LZ3FV&R0MBv6^kjHu@%(sg zgQPn`x72-{ot0Cwt^oR~U+Jn*W4=*+5*a%3SX1mylBgY0&q>?m9KZE{I`O#<~xOc@9rL&ZK0i5-*|yc z;iC3#k1teV=3&*PiOEWkAGe1DA8ae901cDM-F`C4szi(r>AN1>Ni}$M>YmdHbDU6c zjz3*+&DegO(t15b#M(vE|6AAFc3#%)d0X>PU*G6`Iadk_h@8^zcoQ>m2;BSYLZrai zsWE%a@#IOuVc%*kwd97e_PUMB-Z&TR{18@DBu7T}jJYYu=}sv1`1r|feot>o9chLH z3}&F8&Q$nY!Ot(a>(@#YI)Q16Dz!(9z3jv&F)2wt+-1?dd2xP8vSe@8&ikp*L^{k* zUVivzO1;*YBZ-TNIlq5WttCTVvt;jvJJ#Uz(XIji+F0UWS1xQM2N(1kP`VmQJ5<`piZFgRu zo989&C3d2|ne%~%Ma70d8}Xm_*;NwE%$j(zf0K`E_~oZ7H6P;Mu{_onl#{ow(fpx+ ze9g!Ad+<49cYwJ1?g6`n%bOjGZPrE-bHi4)pw>H%*y;ksKYbywFO!^drl>P1DCy;5 z=I1R?nf|Uv#}l~CS4fS+_K2?lZWf(fGt>M{PSLj-E^ps3hr`Xd8oaiOcSW5)LLHEt z)OGe#Xt&)i8*#^}(m8(nzTd@$j`?Gvmm65;v`#27@Y)PIyR@Ymq#2QVQ zoSqlP}oKe>)7#X#!n>x|r$FHtDJHx)tbO~4{jlB-K75tiw9P0e+u_yNGA5T z&VOa%beY?Bo+*{vqH%h5W?;LP+O6$d-}6Go;=`Qp@BPt;=9RVGTx>?Ymo>($9PcW9 zE(NPL!R;{Yg{b^3+cXiC~7)EXN zKanQx2}SQ^iUz8^&NJg5sWaI4L`r`BK(gKS{N@pDbh0I|>+qy&?YT?i)@!(O`rngE zg9C>bs3^&2?%`8AjaHZ53VMx;+9|)17#h55x$IcW96p-YgviRs7az&ytnbu@-&&Yk zC`KJMy|nbBTW)b~!~0K;KXw1JV3(%qZ=~yo2F!PgDSFOT5BjXsdK%w*POo4d7Q^H?JsjtI1)HcktHtzJu8vJ||EiUs zM?!*?g>FlAX_KspzKUb zOfU8)=*o0;R*4%yKGoji0T#{_&fl8{ZR0CZ7ncWqRmwDFM4sPDB4zQ-IFr3Uf8>t* zN&|1SK2i?{3v3OO7OAB0>&|*^GA*_QxQF(YOBtx(jBcgs5{bMELEvYX8#R?-Q`?^0 zd{=ZGsJE&txO}-|Dp7EWv&y%elP>H&d+9&JDeAcwyV~Q6$4D8kuD%0&0Si4-&J-J^j$Rq}O;yM`v}@#R9zy+=W+b zHhd81m#zBn@wNTtFwH$=T7>s|tYiL=9!jRKfk*eB8a$k+>(f)-QDQTXbliUbj7v>G z@jXob?=BT)IH*|@_sQmLTu@GrccZZ57D7g!_x(z(};&u*kRNsV^qZKkShW`oy#VJ*!KDG_R~|#4e8^2-7iOM{dwdiw zuQl~JaV5FjDE<)v#lR^QUw6RbL-L(vWBY0-sLfxpzQt!q&(PzX#@CPz{^R!bM-rl# zidp*qWRnEwZ7K7(pi8D;v-i|IWO!#szr>M}zc1J$fVRtp zy#o|i0``m{$9`Ye3|mZU?WKn5y^Q=C-4jxn3D)Gl3^~r0^8<&uSbpM>ESS$k^e@pQ z4%HnjBnNkV7?4K#WQzM2 ztnp)aiV-Ep8*2K$G4o>X;%=}k2TMd^ycI&x)U|k`iYRf}d%&l2tHAAG^mD+Kd(oYd zUW-w(@RS{;HLqC#2j86zz~#6`>znq)->vGG6?vb0&d$=E7RQ!o+RqmK@z$=1tQIEA z7JR!M&kcy+hK|TTw!Tg;=JYD8D?^WUNXB1h)7~xK;_-LE&&v#3)mE=fvG8#5OATse zzFnVD3-#r*Yhz+C2LQ(Ya-CX@vLjmj@BCB4%*n<&@kf{XNw7x9M$t!^W%~ChTSx6yp1Uw#ABueu=~1g1ueuvtsK&(xPHa1jjA-p7+rQf`2X| zPZQ~kFSJ_sXY_r}_8A#vz)825piy^q%>W>Fa8WL+q4DYB{xYVfrlxS{!{(09_p$~)JbhNB9aAL)= z!9Q{KLZ7N)=n}?KSavL^t<`jjppF;!NBXRv6pHLYhw0icGV}7gFkvnrEL*Z8t*C9= z4bZdsdnOY&*B&PP@bqijt5pMbY`4J%waZYPP2rXlop>!UBfn9LD+C7zM^nt%2xsG= z_u+`<2AAGD#vY~8qp#LmhcpMtyfyaa>p^prpN_(-EYOb4xE|+`YmM=dngn~8_PD|2 z1S;Gx|EKcZ2Z#IYr$i%grIgnv6kJoUTK-77_;-Ln`uo%Mm&N^F0~Q?JyhWKZXlD(8 z)yson3P{-Piry>y9@x6hqHAhs2nyf5l6F^u{q2;kev+woNRtj-&T%Ao0tUPKWkTnZ zZg=K>h!lQ{(^n5g-j5i6=;%1p*&gS?f`3(T!n`==lS_}gl3W!T+~79zOQvg$HT-PG z%7DG52JYT@eiJ>6SMw#yl21y|X-3No>^nboMlHBy*L1!+u|--zhaiAQFZplRY*o3< zM7%udHxt9T@^Xmo6!G5NE`W!eaYuJ;RipTU#x16a#7UJvmH+mx4)O$%k+pJVIobU* zn!Q_FRM*H}0$$$gwUJmeUfW%!lJ`x5Z$H&0mIMNt-4G!2rr*a7mJ>{?nPuYG4t__yFPQm zsfSiu4kaP)FF)@uv>KPsZ%LcXT4`kF9afh`pvNw~!gcvoKFHj>0bK0!+X=W2u3W90 zBLn3w*B)z?r%IE1-ZW0E9BlAp}?F{Oi}>?n@hp8uPMPlqGB1r@LK81 z(iu>fBe+?(bS~S$iX(;sEj5LsAu?=SA!i@$|H|HQuR^&bxkQ_NN(dNu*sCe%uvk1N zNiGnq?`BctV1o6~83z5bYUv;VUQgyQd(pK(7RFE~YZlE%sqov;DVsowAzWo*#;apR{4B!2}+c2@=11Z)0!* zx+~CExUiEOGXS04bwCx9AqM2 z4D|D-_cXNr=#MJUD8%V;neZTTdB-Rl9V?Cu;8I2)xM+(Gd7Z(OEMJ43k&{oNv9H7z z$wWL7J|cdVfr9lz^FLKKbe>>TO4d(GMcUTU#fMcP!mP5)wl|N zKB^`qu>5%oj_|Allku8P6U1PbC-$V(h%^E4m|%}ruDw03Kd2MaClp!)syrT9S@sS; zA~@|GlKKY1iNr^c;^%1kVoN+j>J5@_wgfzIRm47()e8cX#7qfzVWGR{snH|=+irdt zmGzAlzjwrg{$MkBI_g=0Qk{=*TN3fGi&&(h0)!44P0BgRQwLZ~JMZxwm*B3Se1Ce{ z5ZQBu7-@f)RNnqOfj~U zD+!eg=HAw&Q|0$qSBH6m$1(S}$u|f0zQ)5OQ(r<|b~koH_i~Eojy7E&f6D^w!-z$< zv9M}%;0L-4OqOq9P(fFuKlSd?zU;b6M9NhZ?!fAX^p52u|j z<3@*!b>tNyR?@@}r}^@o&pGV7mTq?>jaWd0{d|>SWbeom8qLFAx~GF1xz1a$O&mth z;>C&Gr{i<)jG*eGcY?S$tMPAD5@W>gG&Tk+DdPLgH7%m=Ngfj=R<6F0!}QlR{-RGb z+jFGC&U{@xNk~fGi@kZH^@76yBd=#9$Hldlv>hQI^axBhx~FMHtcsdFUoxzBRzn$< z2Lx6ebagQSKpW{VoN`XKPN|?(Usm6^cwUUWqGh;hIH9O{MdYYd>SKo|DwEn zR=#V{##FD`z?FWXoNzAMj5RzWA&LOSWV!Vr!pd2I`>KsgQ9;%C z`Ki#~^rpJnW1vSjueYAVSJ}u=)MY-%J&*%)?k7lc`vj}`pA!&2CjV^U_bob7WyE{j zn1WL5TN^cfKM{F5KVmtyq-h@Jj=G}R92KFEm7*sedT^ealE6feT=uvBgng<^-#=v% zSLWPgMw58QVmXeyHbP;eqa&%L6dq=-<`O>93FX1X3F}u_HIRu;<3N+pPdu?0urSg2 zm@w~uD7P=>R_jCbI=kmp?94fV#kQMh!*O!_zo>;C(tP1rl<1U=O1i(fV9eOTnV2qm zZ`|M$?RRe$cr}H-Pi$``oVcKP`1yy)^8p4W885KH4pADkH!T&$w^zg-s2Xt zDLP2o{@;cY*!=;C`O5D92Q$9Fw4XBHO?XeCX(F|M4V! zK36dsdcCQ{bZlv4e|d|C<`$!_n2kny1ejg4RX2$d&<(Q?Ie@SbD9}of_qk2LU4o?OcY9 z>QR>PUk=;@Kg#g2X?ckN+Wy_k+c%VBI`e*UcKI+bs)xWglZ{qCHWO4I}9 zwmmQ1c=5jEQv6sm*-?+D#bd!IJTh|1!X+y=H`jcns|78oauH@{>c6!{PN;nE7x*(#2b3svd189?D?Wx*;wxVeM?*o zC=Jo>K>Ry0FOSIv>G=5a3h!ulM25Y~#qkb#k+gXQa?5lq)fWSD5fUe0Y^Sa!JG`U) zF(W@;&xt@W(SSD$oni7x<7fR12!9xpUk9bx?Qr*=#6dXx?ZHkJ17l$Ppk1=SA=eXv z*boS{`@Q&r=+=(7m?-t!K1+LNn8NH#e9zUd(lW{FNJ*SZo;csF^xtJVOn=xZ6c-Z- z)I$#!U1wx;>WB-7eg0=_VdTApgO+=>8|>gHGlgV1-(g zfj?eRA_KF}|6n->j_T15n6wJx%|Dx`wkLqUq#`$~rGu_xr0#E6hekXYjp{$TVGA4e zvrGz#WI1*9D%@5q2A)PIu>Je*Uhc6(uipL>ItGnv3@qok;vUBr+C6n_u=oFnr|-t# z$ReCIZiT?iN5%t1qh`#)OkJ+AFJI}%{)h@3ni`?Wq!;)5E#nSwc6T3lEQRt}*?570 zEWpqcb9D!QiOW*-{SOwgee4ha{u$THzF3Ws=-XfLT`w5m zG=xujBn1q&`qX~w>iNaQ&##f5p1vMNe{Dy)Fk8E`UcMv5#LrQRSVCJY(WuR2#>LqR zaJMPV&U{{3*i)3BZ#G0#LkShqOh0#?z>WQwVkGWW9|qp)DzPUk%>G%8^Eock%0{1V zmNEzw!$uLbJLU#W96_x&zuO(xl&Iz1flOOHSKSap&Y*ACk#H3K534X0yAV_#`uxX% z(1JVS;`eP6^Xl;08z;L-Mo%nkZ1_LRVic_*p* zxisYk3qVaBO#1?P;1eEr-$I6}_%Jq{HEJevc`%+A9}-Q=+CXw3gm#BMpX2g!)?Se% zjO|syZI$kXU&A1#(dP&S+6jI^H|v4ERlYxk<*Xb?ElKZy+ziidK4tD~8K@=<%#6 zS`XTCUhce7>gjEjAD){WtToPN@WYs4N7Xhyh5=}^`hQc?cD;-J2I!9wQNUUG&NW_p z%x%EfDkQ*QV`pW5`A<$&!%CCu*N-Vs`7B|Z2xx}nI$*=n(;bv-*CRM;L_4usFHs++ zgEz5_$pGVQP8?Z3b#yfyx6Wys_;KL39j~z69=vCXd3y811!6}qT z>deG6^5MT$Ad{WGkFZ~;=EwNgdjp|*q5m~-^~ZX?qq?=0ulAZQAjiL_m?Pwx{cbqG z*X!A@sgdJ(3eJ?{3j#l$7S0-bPu6G*d(le=A!n&E8rs zvp~N&ivj?;okzp1)^GFRQSm>1$Ypr?4;J^POOWkhc{A3yN9V= z@AR1-J-B;0+X3{{4zEdjXOuA;VRL2bfCML<6if`%ngTm4D4plzQ43jT18JD_l*6_ngrzZvLpkak|S|HE+#^j@~=ZOh_-BQKPlAxBxSFo4O z*T)-QsZNsf)MtS)!gtb}e#5bHe+d((1NuHd7R7Igy1* zX=R$*bbthGaG_Or$zB(9D+`le`d*?}`KdoL&1BU@@EiMi&#;RN5Qvoen zXRFiPy_vlUQ9C#l9cnGN;H;Ke?=VF8yFd4zPpk0mWb=Q|hG$}1c%az^8ci3xtv+O<3j$ ztob)L_xp~*sHqoQFIC?%-!;<@>eA7%EiSayJQ*G!d*+tuhBKRApuFtl4`Bk>UH!&^U!x8ZyDpESMb=F02VG9?^p?ajKpecf%i81nAC+!Ik=e1!!C zTQ^TX7tHn5%a5Bk9SzXiMU>dS9aS}!O@vaV4}Z3%k&^1hlX7#bfs!=1qszR|mkOAY z5D66uBo$7szx1lCHqPdhH!#@ezt(XMz@U#lD%986c7)T$pds(Oks+zwtU8BQ{|m_s zhdJXHncAU4u`hqcWN>R@R6+AvC8<(QV5`~9gOQSpx|f2h?gQ^Bb9re%!5C~{KQ1A? z_#|NULA^h!>?Ndchm5n6=it0tDj*(X1B^8&KjjY`g*NX~gA!^E4lTZQfN<`U;E`w7 z_dr#8sBBEeNcPK<3^Vk!r^CdE4xYmhX~imxI|cxaw2Sc`ZAG_a(J?}25W+H1wrRj_ znUd`xhreI^J5FZ1(Ab>Ed8Ved9dpO&sutjEztBAnz^<1Dd#}qNTqQGZi_bD*b#(Q* zdKMZ9m{sEhpOMzDqKeFAxxt_^I|L|Xzo^H!?n_<^iTk2gVOk>u(^A^h!q$E2Uuc&b zChK~D%rlPg)E!Q8|4RzmD5hfQQwxiABE<8bwM=gdZG|J%fa~tZ zp<>G+!+$k4p&G^2*Nhi)iniA<&f6rVLWF;|>i%;3Ce>0@zx6~`(4+w}TayZqEqv*~ zf*%?~Csy`3Nja7G)5sl61jS|W_rOnW374mqz2&W}g>V(!$j?KX#I+)4_{9Z&XCNK7 z94_%qe!o-et#afscPoJHZ|uM;6Szn(rwP4TDn{Ta%h0*Kxy?Vk5_>R2n`w4mWF)(x zqrG!+8j0#Iw9=NJhU(5`>Eq^q;;}q>NHpB}Cii5N+x!#@^RJ&G%*>_;z2<)Ce^l=&zW&ydh2i{Xeu)WmvOaT!T%`|EHcu9Y{NYM zUoDu0PK6M7BuX`!@Oh+gR`~E-dO07RN$sPG3F$+htA2rn;kORr@ei=^26DRFSQ z2HX{udH>}b^g6|E(N!#DKc(95*AOAV`#K654IdueUziV|MOk?hxk&I>HE5L{%t9nO zeMzSt>~QKr4NyLH^YqZ~ll%Cmq%Et$oXP)N0x(L!Y=96yPblx+cuWxU^KXq?-|%p`8s(2b($kHN4S84B2;Al5@;heY9U;5e z&BzaK=J+QsI48FELW9lfUDqQ{cdw#Dr|_FfzE#Yb^U5nJm41|k_og?ToHn`p6{aU7 z{2oZ@Jdp|`*8e^cL)W199mQwA$JQEi@BLFL!NP@ypz2nX*s$Ax#SXYL!i3Pz zbYD+?6OxyLayKnI5X~j6<%TaKKP6Yr)+scXy}k8{F(Tw7_k0OR{|KE^r}dffIWaAt z0dOu*@BZ*+P0ib$h6@a^-h2fKF8{Pk&AGuD1DP2b9I1fy&V7#X1LhC@1-1hQFPtZK z$g8b0fsmwFsFxSpc>uhCu1W0A&1ZLa z+@1X}n*1I0@|RJ4Zr&VQxy3k}H>SJxzkRqiyv(?M8+fn%4~?*+cEYLi!aBdV$<}&i zhB(x;o3vd5Yi%Skuy&YmcrNtmvNx;c!(2|Z9D=~HmdRxX6AE@A-T1bnZ)i^<>OOjb zvzEm!XHa{;^)JfTPg6lJakP!`AZu1sBo;RhxEDNA_c`l{ssMYP6 z9qOyiRY9Mqjds$F!<)Gl@UERLMf<7v(?)29?gPM=PW;T>+c}pfn)gMf2Rn@Oq zHCnc6@O~sh0xa&YRJnm&-#ODeXYtgDJoIp*Dl$q``5z_D;X%hq#1k7GH>1>s-(Q}L zr5kY8lz1I~?0Eg!;z|LpgnN#orOvq3rDVS8UKO*1;0b1B9zz{Ba zGT;pJT9O|(CY=Qialeu*8&Z>l=Q|e`q(TXAKVcjYNvPvo8H1-oHaA%gAYx;(%&`0< z>YG{~+-7;E%+R-{xtYoy-9uFPav6Y2RI1l36FIC3pnLYr%pmO}JCEuE&bbn&64%8m zrpNgBKO-r)Yz`NphO$)RehFSaJ~sPS#KWyyE#FG9$lZjbeO}tW6?!S z*$u+MOQL&gJV&?Cun;gUgLfvK`sM-qmsQy<;JQtrvX15`#3gHPZTj?0$Uf0u0+%F~cU}G<@7Iq*c)^*U78Rxo|Z8VVk)NZtj z)?ort)~#j9IfM9lQ2%)dVaFYJ`jh0vdh{4L0xw#6_nIgIXrHPbj{l@cFTWKbRj?Y5LH5 z?9Onp+HjQ}2Bcypb$mM%m(Fj9IyV_1=$AIhXHr*^JBuRNMND8nM^nS>HFeHb{(yFE zv~L}q*!t_+&o&2Fj;IdY}voGGk4U?%nG?wf3x5B(rypZx$-fjd)Af)6b*4g_@zeK?!}?YSdB zp!`;1h3OjUauT+I61)RUPqqYRJ0vgBvO9EGe3#UjE^sM@f6|f0KE{Bcogyk{V1d{2 zwT7O2M0yUC@doL5utb<$H) z|J+Bzu*lb%&gm+iRObX(wfeTcND^S;ufy3PfBwRj`MQId-4^m*wkY7V>yOA>L@VKQ zBU3gNKPNGhHWMNk=?85*l=H?ez@ZTQp@v<%OzY9ONA+IqA)&P1#5YfkC@v$IMknZb zz_LIoXr)V@Xc!JoV`2UCM{|I2E+Q%_ju9-oK7U2tT_yF>xTpZ<%S*d|0 zUTj?@nj_tcpVac)10FhuTwvsmKC^#P9K?%D@Z4@uHZv?O1bjU%ym%g0;hqQ06Kd7^ zmGVkgd5YNuC75wPKhJyTAmDLDcC>xkpmixxBFM6u_#K7SDtGx_bl1zEWSD2xs_-ZW zLy=D|P0~1tT#m$m!zUVe*}j43Z=L__BwxFKWzS4DewF;rxP)c|!~Xu;9np`45&Bg$ zHhTAtk(bmi^hHeAF|!E&RwHTZ`I>ZZy<>XS<`CubSiWiu)7Ut|-+%QbnP=i_vb1o< zAb&vr8;yZ8QftR)n4wHe)@wf0U~<_0vqphIH}1XMF76j24!NduL85orls7x78Fn<-1c$z1=X>~l42UTj%e?Qt2jc!` z(<762V~KV?8H4a2lV?SnMi{on3wS=3wJbn*F3Ijb$OC>#J4GAl#!&BpVV2poA){?C zPh!lLf&e)1uTj;gcboK_l%>uqe{G2RupfRqqeIkYaXowcX|(;p1fG-0Eng4)3v29{ zuQWSfo--G(t7M3mKwMPCz>-80-~8r%VPQ{8S2LGpbGpU$i58^>lFCbeDT*<$&TQYx z{x>K&Sqm)vuWObYo>no1w5FgNVKCm^4;GiTt?B$2)2SvI;`;MvMTI51?MLg~9qL!B zZ)SA%?6wI-F*sH2s5b@p=*Uck>U@rV_LE0jo22Vfn`C%YR6=?>&0_%sHu)9{w^Y-a zbYznCXt8%79tmxdU{*j`P;K5Sk3T2IKb~}&W-+dDFwQ%sK|jIAPcROoEkHu%Fg#{O zh4awP{$g_>lHPNBUej_gC4U0d8$Ah`V_EP$O>kL-W0rqB1x`MMZIQEU@0>>u$C9g?%P>gS{zl5UwF?z=csfSqxzZosZ&dd>CxUR5@D`xMh<9(p~Vypg5>j7l0 znC@RWj6mbvmvF9{C38+joWvE${ZqeNnwui$(D#LwUXHtX7_#MjI~NTH9_irCbpV+6 zr;Fzkr}rSY4N!|+tnM;ZJO%i ze0Ch)oX4uhDBHJsAfnNBLYl~7S#VZTS3+yaH)^|~!+G6K)f*bkPJE{(x4kkF6`Vm@ z5s$GzQZ_U1!7qklU!_GsA?w(9x4E49+;N1UOe+-+K|2~Wr9twvT}1kH}d-tg1==-a*NhNj}& zmPW7gY>TF=`lqvIS@`3X5;ZH5bDjfQhyRiI)Mw*zxyx#1fvZFhcCw>|J5GXwAKp?u z+-u7=(XB9-j^_65N_kx64&N_^ZVWy8U_w1NBXIs5QOhD&YSg|!sakut8JiYG36NVLRMT5I`aoau?LJCe1M$0KRtQh;5{yV5wz-xv85z#`Bg7s4v%^`0v4F8KbG;IASqxoB>NFM_6M`ZCj7>Ey#vM=@#^-;{ELJ!t^ zN&Ztjy7toSoXQt*)Es4!G-nai> zm^+(D9PQ65PaWMA8(K*FprB+;ewEK3J<@`NDL9WuFzZ3~k7n>135&bLT5moC-_x*R zb~U)qk#`4x;pp3c&eOiIpqaNlvJjoz!%Hi$dA>K^jA0(UyH4f}nJ;5CddwFYSa1%31w?eCQ?F$CT@|dv9>Yk#np@f_m?_}7(WuzioJ_>u(Um_zaNQxdGZDZ z0SX{ES(&Bv#gQ;~*%Gl)+tp8mh#?>km=m+@cTNkmpO4Yi3v$5hVA(G;$d(y;PJ#|e z2;(q9q3ij)GzL1)2;JTA?l@TVonA|#CVF&`GgjqV66RpzVur6Cfi2Awbi1Pn_8o#tVT16q)Y3$8|%>@$K7x&w#RJh@MNCT#Iig>8zp zw9>GZ?WuEEHdthtkGWcj69aDXdfVDtx6?O@*vcI4Y;h*=yVYExMvYUR-J%|Rv$zTA zbKN{t3y%UrNT25Ui_X@Yp}nSG_Zl}_X3V$vs{Df>+fQnDlwTTHS)lqS?51jismT!= zNoz%58S3LNt@nQ%1~|(J&Z|vE$*2S@(}e*|8n-mqk8(|wT<7cWii+ukH?@fd!|u+* zI0V4-kWo&Quk4w`L%SxxIqzmN!)z!WWDg!i%Y{cGx_!$_@}Bzudz>Zv0}WirWQl8~ z!cj`|Rm@UgXTi|RetkqZ*y0fFy!j)q)Sy|WWR8MUw^$S9dqFz_7~+f>hN;YJ4<>_e z-x)$bxa%|-u_M%)jr4y;?}Vz-l3!tL_rJH2rC|zgFv|w8x0pua>a61|OtWOOl5WEg zUdbpdl*RSGPBIVD+weqd3J<5D*O2?74&d9@JD6}AMstR~-OhpDcI2;Y-_;i@H2=wd zbZtD9!B79Eg?{&9zFYBE9Y6UlcDr!F8Vv@6flA5Ndbi$53~Y{?2_%4{qiY*O>tD3D zGgO)P@n(q9K5}PCm}|ZNijl2t+bJJ9g4n&*I8x9z7)V#o8@!8!OuRor;p^7gMk9>D z5l(zM!;J?B+=v3bTH(!NIi2ees3;6<-2-GsVz5>F`_W zS3lq;;r&+#;}OZWX`zif;w)aH*H%i259tp!h&-bm$iL&*v7apEeCuj8rvJATeG_mg z6C6nWTXXHoC`O=Hu>3Q5W}OpeRh}@m<{7-J#x)h^{w@-SaJ%p%{q5e2n(mn)M4c|j z#RcP+?IH69aEP~^N>56lpka64--#>B<8S0PYS}f>oyXP$^g#Ox%(k!OeRoS$`rVkb zc!#BW^o!jh?%@{y&!#VC?RGq1nR|Xx7lvPG6k}bKibQ0oZ{Dx!U}}(#H>_v)3HEK& z>Ma_muaK_4-(5|1wEmWCaU;uCBZfGx!8Z-FZBWUN|9nMwOfOroEeCB z*)6Z6oWuxM4RU1P5pswJt|p&i*0%^j#^4bv}Z8N&;pzQPZDW zi}AYj6Q=S{oO_#yYnD_O&LENueJ|{Ke*id*@KiD>=|g&2IO$>0PACQ@X??qnE^PpTTM_AiKzWRl@DD%xx69{7{LCN=J1j;)&dVIKY-RRw+L>PggQCdnunjs~nq+434Atfb-7#fD5 zhMN8UzW-k9`^C2p_Q5{cCoI;mm?z%*xt=?&`+7fZx;(p^+wteX?c(+ak>YNVg~wN= z4klGn5`|ESjSOCPll%ooUL0RaKaJnTkI=j&7@Uvhs8!=ncr4lVej0+-w}8KzS{~ zU1Z)NNYx*RN3)>AE+^b|2Pdf~S+G6JC)e1ee;4hX7MF)E%b$BxJN7bfR_4Q5g7juZ zQpGJ}>Q9yaBxU+%C8MY}A$6NuOO0Gy_chht)R#qA_jpdaL;rD6jvCfFI{Uo+=r+>B zT-r5=BfMbeLoe&*|HoR`Zq*}%|p%d@*`l9Cvc6uT-u$$*8(*BisdHE(~qi-If zX@o|HrMS41np;``61c+Xn7*%tx$T7PZ$&mlLIS^w3u)Y!FQpSRBr`A=%q=Z(3)RRr zt}@~C?N?rRdEsVx>4Z#e;Epu|e_C=v-fVZk&Hfz}exy}KTG}jziKv>qW$(*%{}1yH z7w4jVQI}kfrb6wVH`UKl3=CoMlG8w|r>4$m-~L4}Yn<<_GGBc}heCBt(@o?=VmT`! zH2TWmphd>5Aty|ad>|s1!_B*pu-wh^!LOMct-MldpEx`69mCVuCw>HeTCQhvR%geia8TwW3orlMX_U|y93rYAzTTdA7k04{}z{K3m zPY4fjl4Tn2Gs}Sd=zxa|7dj{xuy>J#eglONiufPb{*sQ>&~Hmcoc2}!9p?k59>(h zFsit1%JdPOpq%yThRWTU>#szClxJ13Y%gBOY=aG8XEFUzreFH*E(Z`Lb{0#VK@Ha* zpGvHzZ{C>w^dp#FCf^4G&|I)@3*u}-3ud&zQ4*Bil@+u_HIpYdS@akx+MpYvcsdZnvPk9?ZRSdjNhK4Rvn&RMc)l=RbQMz zudYruZJM|L_#_qIC1}ZYCFjdySD-(qR^ZW6kL3|mrsbp^~jJg zT&2~v1BAVJ+nU=No;yomcJ1pR1Rz5b=L}&M0Ci|jm={Gz%#r*QtAZU_UdX+Aq4W0; zIN`UhPs~C(OBmpC951~gjjmm~mi@SKs|?yCuM%Bzr;*BUR>+t`N7C7DIC_y~3suin z8}8XUQVS6g`CrTjgM1_RqMA9n8@LSbV@*GKnLvxD&Yv%{KGR_Hb1SOkyNmA`yX-|1 zUL3>+MV?szaiR0msTnSy$^A#KCUu?j2xUvxUF(+HrES{HF(mWLzrRstvSx4}t+;qT zeP!Wx2N3tDW6e0vO({YCW8MIPfkR5@0iVO~qPJ9q92Y79(GTBang6VRW1^(YyMFd2 zf^PS<$a4V&m-h(eCw?1!mH-Lf?eYX@+Wzi1Vft71?!?^MYmw5e#GNMb7d(R$H$`7~ zy_z$tUU%lDH8F{+w~l~&?6B#>9KZ5P4L?-#{n}bqi>~fF7|>Fe_I>K(<;P7WmcO39 zk3<;yy*BOuv?z`iubV)j?0|@lA;qhIm`FmaELD20MH6`Lt&W}@A}y`N0GVStXPzAy zUDM8<{oNN0Wsv;U^YG&Brzym8Wqe#h+-Sw8H&2D$>52pkf5hHcz8sr!Fi)iv5{W=Z zSS}#q>Yd^fl$1@0imSgQe|a!1nZfnqg&ZlPgt6-%R-k+I&qMpbC9o&Yn_tH}e?Qn7 z51i8fk}ATigg+9z;R}9KgZT3067mEu?Wk;=nI4yeO-EK#mDe13`Lf71e%C&q!$qvl zUxq%W_pny9cq&<+4+ejMO>;hR-VBJy8GaF7k-~pcmGPWo5>5&AF)u z4<5v2;yo&&{D*sF_#q1J+w**Y+usMMcGR$(@0%(dq<+8=(v~h1Ak+DP+WK$3x||%l z4}wK3&(d=*WlfiWWWU|kasI);lnNrWDJYs%&~M>5VI}Z9k4qv%VB9RQM@CRrJN6rs zzxw`OTiB!#3)jv#X_2rj2zs*DS_1qUBPi@+vcD;WhTV;@bm#=@v`NBTQqLib_h}4pV9VS znL0i$&74TvzTjZ~%SMZ)Xc-Jfgno@+Jdr`S`~D?w;^Xz6ZmjR?)5VpgAH+^67(M>K1O{IGb8D+tA0b_$4R^|^ zUlEWD=(j}D?UGnUL_~-m*52O79$?*c-Y1!Ptc*`%UH5;AO#2V(1EsJsr}@2ke>VM* zk*CKWJ!93!e#pqHL5+=h?d&!luLEjU<;pi7K*Z#*bU zKOw2M8IU;&J^_?R1lnr-ZPCv*!xcL+;;f@=*H4q9=;!N2rU0_jJs(OYxCH!t#_`(s#bwXwQU&fYJ$_C{Yiw5< zR^8r{P33NGQ{_m{K}*%sX}tk#cJ3xaJ9wrcmr(}LZHbGEvm;AMPdBGcTu!yMv9U{@ zr^cpA26tSOQ=e+k21Jg&{E)xwszXkqCTarV?iX6Db(hAh{#We2G#4mN&$-qpxZ7L4 zR+*+ZTbGMVo$vjyj!9{zDf7(0R+rM1s!7UbQRU}qYm%zS>;GeV)ggd@%421FW#wSM z@+%3#H8*`Q+P&c}ebSrH5x5ckV4XC}+lGbTc7;5hp0ANFsas_O9+Bp6ZCiDjUEc7f zTFtp0H|n~#;=VLSDs>;I)x;s2i}j5GULWs6+d z7%O=C)SUl;x;pjto)9Yq=vOXKPF&iVMS0;{;c|Zb`czbL8DF>oEx|^}@Ba!zz9jF& z*4-vtBpJ-j%~|RA1-NIaQB#LkhWY+8I@?#5l>>fw#mdWyD6Pu_zRJs0qv?wGMRU!5 z7IDF)mQ zob`A$eH#A#aO~K12?Y9t_6BKuw)%;$k=SqPn?$yrfGTl&y`oO*&aPJjCIB5M0ni$} zx&9w>J75k$J6|Vh(A3oES{~sv5wVKm_4pZ&9Asus7JV(@udrPm^>GKd23O8!6Ir&-!maz+;tgztq5r7Po#na?m3+0l@|Z zPOU=G~Mp;i<>qV)RLgK_DS^qWb26K21b^LhNu?$QjSxySaYP3+;g=ZvE7 zJ|_y8l{vTl`MTGJm#s1W`w;(YL~w2eFb-^en0+uqOm)W|k}W&EdPvH;D)(uLWO;eC zztH=&iFoVbEphLC_L>p{qf(Hs)}TQ$vVk72(aJQ7;-l`SyNzlq4uYnk00p3Pkt{q&`z z+7<~7eJlCzG1EtkA#zdj!}HCaRUtk+bfu+aL%KHJbAjj|MZuQM<|BaZ+u^!X}~i5 zKMx48Y;rHX>Aqel`=!L*^o+tggEI*EFtG?>%QJq6Ab#}v(9E>OE9!V}!M`hznaZ~> zPu(8gISA~UJj62n!iR@b^QZ(`c-7+NaMhtaQ|si-RnPSAhc}S&sT(_XFKA!9#K_@E zll!L82E><|25Eb@|5<^kr&p~Pc@#(z$|T`9?y7X@$WyV}sK#<5by-Tm-(PkpD8qa>Zi2oQxvKZt4 zeEV#j(->bMaY;W&aP0K(8>cryi1)Y?fsPVt)Cz1o8b&Ec)Oeq;vOIpzOFh&EZIZzG zdejvAAnq5~sC0nsTlnH^R0^`AhNU)H;K9var6TZ;ir&54QBq?AF{r;O{TA2m#RI zQRutPG)bq?MTE@6Su>fqgp5JN(=P1ioR(pKttEd5(zd{6V<(coM!-mU5L8koY3Z-1 znJ756CSZ2>$Afss?Z3df{1>poUf}%((`7AoO61C}OjZxoD+d6JE4bg~>enge3pGuY z)y{WeF!91{2bpFVJQrXsEi(#f%fpe;*)Qj7&twkFb~OBtLH%uCfPF7*-}1RUJMwDq z_m}bb^~H;a^W2twR#D+mrUkzN=&OyrmP(>Zg4UaNhG4fMpH@?-e-*-E+&CBNuSiBO zINGC~R^yDn;`RUcQI8a%L+Uyl@5~l37!(wr)6_iT(pMK7^}F>8@h~09n^8riRAMMT4#m>!%>ISIi6_Iv^sIq0LarVxv#~YwIgKjA5-!s1-0^mr(&#jBC z@*|A}zfJe)39oXcXuAAMb19xdT|c~+N}_3|clL~oz9xaR+sIaDh(pIY$iBVV)3nMf z%6)r^DjXAfhsCwf{XY{|Z%5%k3_%V-E=t>bt>-$#cbLPD#clB}vTd4I{`-saJJVIF zV?4TsF`K%~vs)0{W!|BdoAxukEmhXBpGK;0j<><40GPJo*3x(_`@R?aci-KBGF#^`xH)0^{ax&9owWR4%RL*p9=$4dC}NQPS)O{Tv_qn7il}nnjK{Od z2*F9?<{>QA5Y-s^tIg&Y>S=pV+yvuzOQ>uHrl=L#CVL=S7y923)9=wC{jK*;=UW_- z49kqO?Pz#5kFpiT>z$;J$_$MDl*r_idCcVsN-mTQNiO6I*8TbV_C&UjHD*FDDHLST z+h6`$)2458tQvGIu1kx=JkCsps$}#VK0QyZIoh4IKAg7A+?sW6i|X?F&xVTd08eXy zq`8$erSz_RBp{`cm-!!^qLV z$k&=%J$9$EQ^sURene-CUqv@fl=czD^2*l2YxDDL?OvNEuF>_6e|%}8uXg--jb|)< z<)kX!4tlWHjUz$J!82?(b6?rV8|m+CjZYBowH64qU1&79?;KpM{p~FM4;sMYfHT$2 z41w!yO_q+G=c;Zs@Yomq@uasiE;9E`KzyBs#}-^x7wy$G<0N!Cw)dahUAZHF=m3%F0s?0lkAIU-*pd zmpX#~GFE!l|Df4T0L|`xI5{Z(!YBT6@rhgWsylgKQC5toA4#WQu@a0Liq6cpv465* zF5ZH3KLUivK+X6Mt)`U<=f^t{N>(r*lF~MgXlx9-G<0sAW{rYo@9tlIL}<9Xm?x92;@(wr2Bw3fcB!^=D*DBR)3m>YIb za_giG-@@v9sPFq_yKT%O=+4qwZxmC3Dyt_K^`-4Vay9|+ojg|ie_#G!(jgKIb32u@ z1))KQdFZ>z3%o=3(}PqQSbnP9MPcLXFJpO!pkqm0+SVA7#a~@}u(9utxM!N0BUNl( zZ63X1%+Oca<3VPoULY3eIrDmlED|p8J17(T=SLiFj&n*Zv(;mqlXHdMeTIgYO?zX~ z&e6Z~wbOFkQ>+z1z6ZZ}8aMLO4j1s33tp#7p=43;Rv21`obi#GZKiK+@w1{F$$7Y< z_wly6%=mxIhx)D^hM7IV_lKnOOdc{xXmVRO-=VJ_lX`kZoO*`oO_g(Suhs>>ToIa< zPQ94)zpOcP-!7!1MTQ4CcN7=gT4NVb&o{A4VN64OMQg4W!+;P3cBO3ZhMVv@E_RsM z{LVLVd?8op0Ocl^md>V#?;TW8QhndM1@U~l_wBpA3-WSvCOa45x9uVW-DRE-Lgbp# zA`Q-OMjZhnE;B6-C!`TStgNo@P=w>bc%}2eWhq$%1?mw#^9`%ZQ|uYHn?^7e6%HxT zPIFg4c}k&`q^7Mm3dXyci84TEDZ)G36FMz0SJ%TnzH%j$r#zad1BVgyS8tk{> z`q!fz*iebRi=@I(9!Zp0mNUc`yy*&%XPl}pI+HQCxZG>jy>xCfyWDHAykspeLYvi1 zNOP*Z*~n4WKXp_3K|^G9m3y)OQ0HJ^9TfH<*lGg?*qv zD}~zkKyCpmeZx*Zhxf+ARkk$_NEKdUe$JpkN2bUpV4-kKa=|C}`L8o%rW{@^<7a%c z(Kq947HM}S8&=(YavarA`a#{>KgCf@lJ>r%_az*sD0diStvU$*z?Y+lKB31`YJD${ zCP!{WSWn+Xv%fI7IgdX-*+bU(wM!Hs=`DSzcABEYbTcbMaR;mYx!B55xP?^A1_F0^Z`QW|HATt8Z@OSeesotun5+KYu>RtAGV)HTYeS>F1Sai5^JFMatpL zfha2cY}5F%?<8AS_I$gluys7G0;8EE7&XIs@Nf9duh3uVf)UCF_1b7AuGd^An7}Ue zK01x#6VJbBNKLfR$Mz*mejHtW#8cILf9=_5SRZ}iiJN4RZf*~woV(-7S3myPM*&a5 z)c!1`*p7?pL-*+UayPm~a~}!dPR6T6Q`0ZfleL!PHwIsAK6{uQl4S8!Cn|H~=aP`! zL6Ev>^}ATZQghnyMkYze;YM+XYKX~6_+Cr7eCbT#AMwc&|JR6lwYAAwEd}je?`cyB8Z{N#mblck8ZQA=yE;*OwP~LJ}=)-HP z{#~1-9!FXte^12`^!R-tXWE6>3UX-Vp7Y(Lp4YkRsx zEB@+y1Z>gxKAaE@0E!{NS&we@#&px?Hm>m?40s6ND&)ZC3-xnzj`NQdr4nn|gHF~* zIH{}bgjlCt*2TJ=d)iDFRuM zzap8h&IJOW#?9iALl6HGkJtdeet|lr%en71nnzocLe#5@>wdB(Kb~CLdDqxan)Vvr)`ofu? zWMiSt{}Hr>NPa1#Tbh5|3h`QJp#|v5im80ZW4^pBu=|*9AUjtz=C`I$%iTdEo?9ok z*DjViLxGM5iz2x+g*)!Yt4WqB5!?0nzlYBQLSNOw(ehpClHRpmlFr#>N#;c--}%-f z;yh=d5F>9fS2s2pqXq7x;=4D-j1o15#?Fjl-`ZPfOmxyW_v2Q1I5}qGL2#vvzwT4Y}$l^5{BcA>~ zgS{$Oakdn%j4yhD)s8am-Qr#w-Akq|ucPj)aOy2mMg5GRv}ka($#DB^a(21rUtV4P z$mZ26rA$0l4xjt7`;&4T=eR2bO|)(c`9dX3sf|52IBdh8w~csiY;4H&hx#^bO_qZ^ zd+j!p#4)6O@pW z!mz4n@iS02Xc;h|4FJUlj37K9x;x{A`ikL73T~U@xD;EjfX8g>x0xjPA5vJQ9oQLd ziiAz^i)QImhhG)==DicpNaSY_xCdwV+yR~+)AUURS=4&~gmSH~HZrf&@q747xyxOT zwz0O0{x;?fP+x&_Jv6<}b+u3JncT@7EZ2R^P*$<+>3wwK!{-mFCrp~3;J*=_;N!iCqA&cK?yW*P(y!=4S8rD2*Kz}D0^y9Tjzz+oy$JQ*RM**kFe0>#aLAdKp zuS9O{NVeTs6XKAkq~&;Pr1a@Y*Zq&Gn{%EI7ZJbfU96vfGu8(*9!7k+UJ90pkbO&b z7HqpX;I;1-`T*YfY+A;xv=V`jXdE*0_i5#2=;w7Km zZzh}Ob%@4ig*hhNu7}zx3ZY}LBY(O3-PCx0Q1}LE>EO?it*_YewH~uao2yBjLi-gl zFna~#yl);0o<>e)wa>Of$R{p-J+@EP8jQE8^J)BGD(?8LRy(!WIwEm%F2D55ZLes= zIeK&(46F7!+FTA1MSBze)Mk?03>4WbRK6^jsB|+U-JuMFEP+*x)%Q%}N0ju2 zMIeqN8eIt0L}*YQ_{Qz|;q9G49?#{a$bI>g7|21`YwmJ!&tQpz@Sl;r5DsPq(s24s z5(#+cvOrXw+gQGKuGe3v3|y1V+iln(>7z_ME;E*1#!W~K!gCvfv$3@;dHPi&woc{} zw8#zl_`qhYJjkqVtE?#}2_xfu97)Z(8-@%!$_DjBGSE!lLd3z_<&LMU^`(s~_LKZ^ zNoS${r=f3R2|6U9+ee!d5lV0AJlKB2Sd4@Ao$<%ckMS4Sqm_7F6%L5oSFJvtgKJ0m z8Hc?<@aLPbGyJ-%JeoyX7i$9=oeMy9^2H? zyu2dr#$9^D7kX1{)HhLd$IXDFp6l0Hn+yxf`5|TVR;2At(4Z02liM^h!QL#=V9k0B z&SCRTMyLBF{jTi*9)B8xFKol5wS}(WJzHGu`~DSXfJ`|5avfRURlm-STCsBS5e-USI30SraLe7t`9{ti|uGg6G z*x`q}@sBPTF;a3$z+D>!?L$fWY5F4m9<$sf{}c%yBGZD@oTFTz*`MfMy>hVhLh-gk z!dCO1v9o;ewN{CqAK)(#Jee!6EN{y$mpwP%nk%si@G zlnN@a(M+-*+6FU@m(z|cu_@9 zIk`iO5otrX1~_SA2RbU-U3?PTmeC%xy_nJDW!ba!T+XFgj35_8Dn}rP54s&jxAnU; z0>Y>$8${-h&hU7@*K(rrl70STn5~Phd>K=YFzae_!u@Yjo>a$@E8RSvRjS~n4k|y& zLedu26ImXiW(3Iha767z?{WtHT-bvk4b^ARiWl;CHInIY>+b2GLh)n@AolxDLuM#P|w{zd-8G)+fcey1M4UTAUU}oMC8f&05P#usad6Jv4SOV)l4$eF0p_K1w?l?GG@$i9XZEBk zUlCToqtN5>=H|k;hBFDf;(2nWBBPeGP27sXCPqoXt>h}GQT8CY5pw$Z@w{94%$#eJ z=>j}r#@r)*#vB$nWA2t=YUvWixbP0hDUP7jevU2<`iZ`SbacgaM@*DK2`2hWa&)?> zH|c4H)GsWB1TS;IUfT5 z4q^|XUm+1=j`$IF+P}v|$k`>?yn&N}TZImvj)(f3=2?>PMD`v!3m}MN{2?_D$dH<6 z{D-zb1C{*uQLMUtkYmhU)jX2PFx|83G0q1lWm#5_7226zm+C!0uq>L6iYo5 ztG4#GnQq~QCE4U>pM%-nRyHZb-MV;70-W%jN+U;?{l#{B&b)>)zuzemYO65pU7|W^aUumRW4CY zEmH)!e5uLxVSJqi#0lCs5|80i=&bL?qn?HF=yOZPnl#*C(aC0C#T zLC?I~d)Y)s_mRpb_SuEKJ>RJEWglz zs|yCr7qo0P{*zhwRZD_}za3DAj*j4l>n$s{o z_uoHY@0ymAfa(PJV6@hhB@nA{an7?i4_=yB;ywG0Mieg6=%t@fFBm~zcM-iFM!Y44 zgwV7M-1#VSq^Q=ziKti$I|w_$)x?NWisJ8~z&0bc@b!3l*30Hdq;A%-F{Fo7Q`dju zn~B&#;ccOB4IVz)S5Bzy)+CMsI ztz7GJA*Am51Gi*br*W6vE{Q&U>!)K5X+tBtqovJUi1CnA+lTqICZg-yk-wxI0Riq zdkb5aEB(wOAJ1@6Ot_h&TOk~Ty_bvIIcc%!PgEN($ea@VAZsm67WCb6ll!uOOAtY` z^ARUKMR(cdGkC9e>*6nuCindMBiiBV~uO5T_QPVc^DMdHjb;EeH+np%^%jLo>5TAmlIe~ zA1R_ZPH`558EB{$lC>pX=Q+ql9CA}x?0=?+$h^gg0Y^A!y{BEnR}DSpJCk;UE=pF?uh;hdS@4(4$VWK6b%&EtMWF&WK6A5F=`L zTVx1VW?Sh#T{I1`rQVKdV~`%OgJ+8MB)4^RTyy;B&4p#P>CUuGPO4jgKkSmYKJ=Xv$~x7BI!1yfYu8a+ zp-qHGRPw1LLN0k>`O^7+-#l9SIcj0(bIfnwJsG$9(^~>VKf!(RK8jvCT!oTtE4OOO z%ya_*uV;;~_2&U81ddaoDuvEVW5Mv(79n$~%|KPZgbPdYh{azj0V zVN~)LO!alrB&-H8o^DcwrA^jNz@J}vYiCHQNA1?D4k85$=7Fq!UTqk96{(NZ$q@IL z_t}Xg%}y+YAoLEp z28}KNT>Y%xlK2ZsT07llq5A0NmENt)t_U|Q>}fZ4SwJ)^CXn}++bf@-tq$WC%vF0b zH$-|5nAk_l+pf;my@Ry6lw?U4{M(7>mB`pp9Hg}o0VX+0;e}9UT?GirE0lzS63Ha} zQ|!HQlbM&(nabf6^FFF_-^?3C`aGmJLXt-k?C+AE6|PU(jZjW4z>Qs0!Wa)w}uVynL9g%T6c)A z`JcrsSBP{$n&*%*Cv!ie14JojDxH*`t(kP`4xv+Bb|_E7sHvM9QzCdX8!L85I3C7| zO9>?)8xoK*pBkdD`+jT8Go4bmck8Z1S??dtJS4^4HM}kpFnF}LfDeNYbs(gsZXv1WR4cNDe|Nywyy<+1 zu4m}hzP2KZ7Y^yfL_XgRIoG?Fh6_i}c{flTu)s)Q56I+hee@QZ4EOx#4LiD5MDoIlvbNK$ApKfxlm&K9!Ax=C9rMv^eU&*xa;FK~ zPcd3DDwGonOk|agHGUIt3VQM3Mg&D=!??)cZ$W;-a5;x}F-sQl|bg;eQ4wbmza~&CgCAt>` zM{I}cl+N1q6o}Q>Z0WLolWm34>6(!GHZ6O?S?IfNU>F5X8<-#pjEkpt`|9Xt?LxF50(~Row&s8E zN_XjjLB5iwNPe;{r4wb_J;^DN`F@g)FsVbb9NyZnYqYfii_9Gl4d^;9EIj_uMiHEm z$|=}4szZOf>A;{Z;+pn#&~z> zoCUNrRDyf1kqK3(ubI$&4gx2>A)I3A<_seM2m(YM|NaIk<`am+8fe?v)_XkBLmCV#IIhHwR;OP!O->J(5hma5) zGEF|1b9mYfTVvg=0wN~Z{(+@iy0QiTYm`(O4N{_a#*Z+@vFF;3{M;QXG0LhGF$N$- z3SYT(&{JlcU&kF;Z!X~PfR-ag)6Pd zBOV6eN0o51V$_?NM*0>AyJ8%6%>jvMX)}lDBI+XbGA1(haz|Ikm%lL|AypxJaTl$o z0EfbHL}N@gr7S8<0z?(AqXMDOdrMgWFbjXJICd`VSVLCwunNltR2!d6R0}^}D1>20;4WX=BaeV;L()dj=sa*LgJ>H(=f89OjB(kEAGYlQ6d>npT2W4seF|MatQ#%V5{xc;U zA#qErmj+3yK6IIRJWAXyYdjY~E-Jx*Ka`#fIj5gYsr=-hN`1XBav*pqYa3qJ+` zW{Q83XqEycaZ+#H?(-wLm$ibXxi03&C&+kc?iVCd($g63`W*$vLXa;9rspi}hjO_XJx{>N%4_lX)t`|(`t}nn- zE&Q$c{NU4F2Fyjq^}s^~4FArRKBPCo$4x~+=vl*)%bDsZsOhfUyG7piJ<4-Cos4{_ z$3poAH|ejd_{p2;lNW3z#{~ zRO?n-b@p&_j0jO74JzSRy;MjtjxgeaT=?5)U2_OU2O&xm!u|vWW;mHw{rQ70excI0 z=azq!2b_|$@_LYV(32j?8^j-9s=?7H^{_u|KZPZxQm=m?MJhH2QGF*^30)ub8zl)% z!1huj8Tv`052@T#29(opvw3lNaZ>7oHorVXh}~SqQcGyEP+C)^e^iT*F}O2d9@aWw zTVh!HX|2EFG^@fT2f*Tc5bgYCn`jxTh5%fWgG+H2tW031do1agZL=r)Dw1bt2^wlEYJ6WQhV0z?Yq~Rz2!_W>FDO05W$X zbIXe0gmB-d#0Y_Ul{paHJqvFFQIJWteGu_D@3`%6sYm7T98SctC-i^^35%Ht9=LWb z;F`LU{If2sI0GqpiR*`(b$f*X%M!nB0}{H%<9`UeoX@M^b)wdKlL0RxzOcVSiAd(; znap6C*ojd%dffLQDBK=C`^Vl;RJVnUWOhEbV#o8#M2&^2uc&Xx@A8E##ZZ1k0YISR z;YZ*xz%kk0iwgTtNUsZray$$M$R&*J6zt&bt;hoYNzybnc6&Aj3=;>p^|fqKE=TU| z3>{;~E)Zk}16NA6YG!s`Sd~kJ57plfdQ5hmvOU=vH7pnDzfHyd#ZQwKIW5aJA38um zvT_k33|h0^45yb~P6(kWz-UWh{__KD$NzV)RC?7WP%mbzA*r`5G+@F(MG=P;b zLl0Ph2l~1HrLq0;#{O;ZChqmR66v&$n23{*Tw|oCmMX zGrqxx%#oKF7gVT?UO2-P$MUu8W}w(Gf+bx3)Gfl#pDmQ)Fzt`%Ci00{L@dBE|86KB zfc)Oxe2tlPD7jJjw(ATv*=(ygXZA?Lmx=RV1=TNx($`o0C`~I0WUQ_B&=1a+ni};bu_D{8^36u1YSh_f$=)JvXNlA~S zYBuPG5sn(LBgPDmV^wZ9{Z)l&7rldCl+i^DC`J}M!=$u5|_9}3*dmOA1A5CNi_ z#WrYW+M%71q058u1x!2~2pD;C|Fj2FT1Nz(cb+qqWF@=Oldhd-Ot->Yess>ey+S@g)O^jJMI>@AVw}Ro|lcEXy5AhPU(EFLYU9cQ+;qsnbabY1>p==poT_Q@e z2%zlE3z2b;_@Oh;wHpJ}Yx+Hgg!q{?Aq5uvl<>n5)!Z$Hjw>3K^kBaBx4=Zv!n?*V zy3&;q10ZJAOcy^ttJzKzdyg<;d}Z(g#&N=jxL(L<4T?pS@^vu(#L^rsfeFlP{j4%w z`--70jlaK{(5zC?0~$okI5AB67Kk3n0IQB^boJ*Z^x2f+WFMzs5M8N9ftLe(SC&y= zcLqYYv(L5(;}?n@L!DSc>##!5oI|pv6g?(G0{cxdGgGaN@qgFu45oMiKdH6F`)Fi+ zWD`Cgd?qS`4$Ep0N_m|$@Vs2JY`j9yqUO!KkJC)O{%oz|$R;h{Blnf9&G)?k;AkOg z3wWZ_dwI??_tf*9JgN#gUMtTz@vAgvsQZ9odR{1n5y-Fs8O$R3Oi&KM0$1`z;Hfd` zuAcG({RY%Gl;Poyn2R^O=g&T&my~6Edu^Mw`o6=VaNJh_w3A#u(z8vn=+nE9hzOc{ z_fY3ZDezi95G=elN!G*`{B9-am8!WyG6((?PMYn_Ku;1h#g~JwnS?Th^Gs=)OLaj> zOrp@X@+fc)$2om3y8SN?DteG3guRU2@uv5R`8I_MiwH4=-|@q%uPD!TnxO&;D#O`o zJDMo#%^Ky4o_ib2c4*A$)m)Rne4WWvZ`L(6!?;mW-00p-J4MRw|~AXmy?6ac*r77LunCPF1ppYxn7|L^rTM)6} zcfO;ayj}5x8?lakH&_YkqTas6PNl0Dq;kM^Az0f{X%5@IV->ii_S2<@-o0n4w^cQY z(d`w5Lyy=hXGpxzQTsV<(<A zKX!9jPuQKz1SBMUF^JMA8Myi3C*O@+D+H8e*WPtKQNVLbP=5_*_#(SB!}*cAFY~w! z;NpyUn$v8@RU&1sFEM(PxSti!A&bZ))X#u43*Ks znH&nD^l>2821kM?P#`#o2YHUfY||rKZkTanq>q(b_Ai9i{mk!40d9j##5n{;klp54 zmK9SedA1nNg9tn02f{$l0V5#Y-1=8tTRG=P>nXxp7J1E_e*0R}A{0Pt4%OQXqFmo$ z2izbxbPnb)=9&} zA__mkb>zfCLvfK;=+qz)ZIs%Oxm8#xO~O%1V;D?V9YLQC@;}W^g&ZVru04A=yp_K981W@H zw5q@4d7#SK`ZB5M@icmdfd;Cp0zrF2pK$WC2`^{&^}B2a@w_yLs801Gt)pFm3%jdy zOc|oeo97h97GyuD3;e3|Lz))l3A7%>@F5HYJLtR{4{m@kal>=%{}+4j8P?RcwGG?Y zps0Wa6eNh~MnOQOmw<(014McYD$<+OGy-;{Z?^(UNfeacdr62O5T%74Y9b&62oRD0 zAqnMMxX*K)ea?Qq_s9GDT|c}Q7c$qHYmPDIJ?>Ga>seaWA)*LMc*H#qNZ)>04U1cP z<{py-Rh#C+iT*D)WS;wU();}Rg{)c>P8|>YEqbSWo6=FDhjQTIjmFdn z*bCg`P+XV(S!k=m?WX3y7QJ+-kQdh@+#_Ni6$lH6%r6xiR8QNzwz0S(6@lv#F?dfE z>3AU zlfZW~MrbY?s!AS9ZPRIf*BE!bJ_Z&u{sMbrm#IhXuTzbo3}XupfWVi2c;{WeTIZ%bW!Py zw%{)V=bjm;0OvNwwWzR8Mt__MpKC2?2ckja8ZDPxKISPH$XCt<1 z=IftB!)XlGg?Y*+*>cVIYA!$Z?+>%Hwth&0;u@S${>&=!QKDsptmo{M^w;Hap!TEZ zd1SJdOKzqq=A)@zT8?d*s~1qIA+G0(wd>E$5mRoPODTVfBc5Xwv;vc3VLAl=xs4#vD;kOiix~7kn zDObC?U+E*`ZoA+3Zo=Jz(IUdbXY~}_dqI01?K$@R{Hl;aYBc?YxB&SJOsDkA&W1Nh z6aX5)b1>Xp64KH2={^FF9g3cc-n^ry^K37n^%k^Ae;CAVoZ#fhD57Of%XFU1{Pg?P z`BSGK2swBa%Is1H^*AUMDm~asO6;1_4-x=PzxsY2`vci|t@zprLyHMN9sF_iouXbR zmmckhydIQvF%z1X9z01(DIJm(7ZG6WdGcGzw8PbdG83N`;Rj>jqVac}^aG})yi*ZJ zPNO4)hXm4N8txoyJhAc+bS-rM#1x=tjQodT>9}_JKFMxDdjd~fy1Mwq?{U%8^R8d= z5>`uur3X*2_?^YI0sZbbV=1!sk_#P#=3nU^2x;0H6yT*1sBiTu0RM~L_3>Iz$Jq$P zDJCC%&4O{FSV%Pe!d9BW2JNb0g4x!b{ro6r>t^j)c zgckjybIld<;K}~@Z2^Ap4f6?*+qS$UzCJ z^wmI}%EMwr5Bb2uM8l6dol_h+Sd_Bi46#`F`6U}+1E~vsFBi@f%cH+PwMGaNpD@0c z?TX&{O<6ay#G0GrRw5jRX0%lc2;S02WFC_kR3OFj9r#-T$q|F?jHO#W-+0(Sw+e7p zuImxgnUQ7vpKy@=^3y|jx+#cfU>Nt?8M*8!l#pjrTakN1qk&ynNSd1O@FxZPDo+~F zgx2QqAEnOJ&KVvFNGvT-thT|(58Nt5vFfA!>9`T6(kxQ7sI z*Zv75%sP?XmUO;b;;Yl$-UyKS0+X5pWB{V}36SDQaOwli+}G{+@vrf-X&3444e8B5 zm8KBy2)}SNY$%3sQt29yA}hJ~5O(<>Iz`&ztWqu9zxCE#NYkPEHwt^-Fg3I?b4|f zu@UTioZ!OG$air+zTWbH9FY-!c_&MHUXk-!Z))jMs@S~1&b~L3S0ONk9utU-3}_tp z;*P%KRmcgvs)w7vC)jAW;qaFSFREcCThe#$C=Ey6xvban5ds@vT}n%02u!|GX4DI~ zFiTI=UGB>Y6A*pH50trpVaqb7oK0iWo*sfk4W1>5UlDJ3)-?_@_)TI;{Gy>I$ZX#e z@#@SNCmn*OP`qE07&e}f+uSlCc(Xb#M#g*J&J#k{I~#suyldW^aK3cD{lo1zm;KWF zq;Es$>!}94gX=Os)!w|-4kjdO*PBlji-{`0Nx9kF>TT+)O#&3fe)lKC9WOt z*PPGjgnbb_Ztu(xsXTtK5U8YcE6)N)hZjTVB9FC(8m9ez``x3rJ`aRm#pvrdtTF}; zP`j-YgS0514~s7-r|-{|B!uQ($Z`XoXyI`o~G zaAgf9V9aLi%dQ>6e>fd^UmUN!YJz)T_wMVfFAagasP{+wk14@YoXg5z2fVX?zvUr; zClJm#M};}pe}$CDFLsQ!*&e%rDY+vzc=AH#ta^)H;FV*&4oaeJd;E1DFDU;uqdwel zYER2iVqDDEzL38qgPU8N1>B62yV^0@>ihCGM>%4%lRYjKnXpsLs|3~we+kn^O#!-` zt;?5uq8n?eo{xs)Jj1A9aji$i)14_&k=8%qGhO2|2ENy^UrsZwu9tnj)j!P;N8oM` zUC&p#o^5p534_k4BAR3arOlRG2ve{5rKAe?&Qy2PKuCJzznN z@>;QQg;yA(Np(BMQ=9yZqp7(q&DPn_B;RB;)Jt97x}eWU{p8vE(pYTEn}bRwdsMOf z4A7}hvJ<<=>DUW~OVcjr&`>OP>V+4~tX0qoix$`!bvEwR%XxiBmKu|is$V@2 zXuzHzGDWMwSTD8kV0z!T7UmU`&8HFA|T^XAd zY?amb7jW!QHH|%ZkFjpbk7|dVoFY*e6mz~in?_v^Voug#UUfBG6o`EFI5j2N?S%LK zx@$}AMZSDj&~F1vsrT6BFPO~e9SkpN(-xgZMU=4EbZ5{EZ`(Hlh%6Rs_P5oicVML< zS4Z4sNL%_XZAwq-^}8N63^A_05;Lg&9i|#+ssog{D(@ETD!$SQ?q-?r4envdvTvci z-U0OE{ce8tk8Pfrq#RuMGaT)g&HmngragN30Q{1aq!)UBpbqQN9ILJ5q$&6HrDFEv zirY;1bc^tlZ~q2i@6vC-K1BT5)_S~G*Q_Y9pr6CFUk+$ysn*f#cl>aSmv;R*d0C2h;COe7 zyS)!3wK0~DvP16TA9^Mw-dQN6W)B^%P@P`5n2f*o(W=Nc4}5MyEL@2XfA!<&?918q`hL&-u_eME7?20?5199*TssYR{5~oRpZM4* zIJg#V{d|v3xv=l|XMYs%MJhKa3!2I|%1xGt5#voE0Wz1%#AM0$Up2te&7?#R+|k1x z5-G_Md#av(Y2k`@cz0(V;G|ySgry1Y;)!NKr<|rkJImCuh)NN)sZ(9%AT;c~mkbY`!7zpy}npGaC)T zSxVX3Q+IWJ(sdb^&dI#}9v#S)q-%<<(!u--OXO&f0OAbo;%>kAJ$$`}yQqwXn)+A&QljN-|DbYoD zvb5=D@vaKX)3079ztQnqs4W6I9H1XU2;s+p(;=GOFgae&TEyR3+sa2#XYL^;+#2E6 z1!(+(P%yw2e(PU~qUKO1O=l`TUIQWvNH9=;pX+qVlz8-3HalK=$efS$$6A3GvpP$; zEpM@-iuM91iAA+!*=+(oW@+#UvFXPhCmphYb1SG!0RL*>)u=$n=}n_|XH;ZA>S*dJ zy7MdD!6q>@2)m<;(a*&XNMpW7Y<-Uq&J$P?D9j;;3Y0x}Y7tDuC44XYkkoY|IzsJ% zdLzB~{@1vDBl~~GL&f2TKoaA}KBnH8?Th3}LuB^6;g@#!uB>`m?s@aE=d#VuK;6gv zwSO9Db5hp#GHzdoG!9Huq|ALcfV_Qj%=G=MwdX<2JiTQxxb#DrFJ-dI5bmp`oQ;O9 z^WrD>UlJ4r5*zC4Ig$}ciuWBDqC(Pn*EVjvCGHXs-6DlQR*{l^~UZv0)>Mn@fOz9pKet@ zw>GpxdR_q~V-J|sXKp2WZoE7&jN>0)AH08lx4^6C9jVN2qq#@K4e>O~D-xFi(l0FJ zG{SSmxb+Y1EKYsALUqbtJRqK96cOk7XvEPo)%MC*=gkx58nP=lLA{?~g`SR~FSM#W z3xFhgukndyyE@-AATGvJzNq?G*hJ&bD$2md#mQ;)s0+qLmm?ML5=O0ldiCn0h%wCm z>bp|>Iq4R2mUQ?kqInXmcTw-T7-8~+pJ(eW1^F)}uNvMGAXU$WW0BYS&qjm}64Nfw zffI)!SIe5eN{%Pt(uUlQnw=1-{s0b_E`P(fr!U@s|GBEH((sM?dCYB_K%KU|9!`Hy zL%&5A7tNoEi9mddZUQv{6{`LdP3{*?GSZ5d%lCdn8}JVX)cD!cpP5eC&TZ?XS z-bl4nKAYjJx;#zubMM4*xB<=k7S^XzcJYnw2%bLH-&~K>#Iz{q3fL3 z`cqQ+*3X^w1DNB=Ki^>1KoJ4L&qQ$X!6%+00?)fh(bn$m7Qn%>BM{>q#;9u`*{f&8 z=>DCuN{2WTX(pNkTG0-dyCL7AwO$un?qMAs6Vh_PAD&g8GnD+USD}iZK5>?`i~{$9XC0FlLlB2p2l<>%Ij`JN`H zt49u^Ry=XTSw&n3v`n|P@WaaS$g}#$E65LTga?O^DE$|#zJ+c=KhDQqwYDf3-C2-s z;-NS10d$n$XJAn!-e9}RC_8oUBMSR$YQ1aJe8`6)i#U-VI1T=MpAVl|!S)56N^LKB zlupPTIHQ`jKZiPSRQ~3?LM= zK~#4;y$ne&MUYW=L>2vm8z%H-aGpbU2p~-<@i3FQK&z_?Z#ez=h{)PX&kRl=)r&$# zWnmrvThfo2_i0Wxu_)rty;c(;c;B?&u{<;v+-u-&gQCVl42IOik#CeV`zx9oxJh4 z%+P;Lb-D>iQIL52IPNhHC^}b2R(F#54gaB26=M-0Uu3?ukLnrZs)@RL>Eq|-z*F(d z2afOcxotYbT0RkbJ}u9RME>CGGm>+ycRCl!u=KP%Wr|{o5O2&!k`)zLRKW) zJM(Q{v+MpAEd6WV?+ybE7W@Q|z~K&fCy9X~QWoYmN&ec)T>D6@7g6WFKBl#y~olIzf$>fS?a?}^|8X~&#eZhfD9VYp5?;kL%HU) z`Ez6bMdUD+ae-r*VO{-(+9A4-q}GwW9J1ipe{D0zoSDGsAVrh(^ZOfB|B2Jvy>JfyiP=tNdJ!$^)lzQSSKuPw4`!dLxMiB{r#k@`ppexR zj8uUFJF|+2m8(T$1iQm5Il0|ysOR%=xyPH;#W9J=2eV_3H?~2f2TC70 z9638u>9gG)r`&_(>N8xf$wkUje1<>8>#p62m4aPg0l z$aLN@?uYT(>TnXIV7gi4J4=XS(x{BPRq}lJ^O?R&;EyfMhjZW9LQGS`OOmaS`bq~* zeENJVQ6DlR+V$TyIr;Vv-+hwRk@611Fs)SRyu8>=FoL$wGe%b{R-qk^6Q z3#BsboFg2EsPZX-m$`Wn7svjWu?&F_I-kJkc0)3LIyS>?5o>MS`qrBLAad8&Dr(Z; zVVoB;HS5eZN)I!vY$7^|xBScKfH^Z1-_eI{ibk-kEUl zz(TKea2_Ombyg4dh5&A>JIa&RL4J)n(QlgR))UyL%aOc_5boy8L(W_OvIW3Gl{k~T zft<2a{^Wm1WTyr20S^fi89H?Ggl)f4I9iL)SUGv0EUoYn@tJ9XWAf$tudL+4Ckqhs zJ$cJE5Et;9*BR)GJJWDoL+Trma$>o+QbO#ou*lW<;I)4={b_$6-OReW3q7u!X>9(6 zCuQ*l$lPNYF;e?-Y~s+Z`5$EcstwZYbn@;$bLz@d!sRz?aPw@_5V;|pe6Pi{Kr;>I zI3wmd!uDui@o)xrX+x;oqt`XU@1MP{O18hzNpC!7P5>OtsqnPaQDTCO_81BK-PN(K zD5|ysYljR(NX7ML0sQphIwx7%um1N?ohw8&grU&1)g9i0AtN|OUEzI}Y!Tj8Nl2n_ zyBiIVmh`{P>UbnDs~zKgQ2OxaD>wx0d*GJv3kM-y+T@i@ix56l__9Av)wA4{n3i?s zR+2}5vMPYHQ!4!^h3DS5qewTAzYx139Z3{T4;AtGKWuL`@8;$15#75)m?&4)pl(05B{UXgh#q|D(In zbj;TYxb(?aRGc={av{oc#o@LNJ}c-a5Z; z5;~TVr5rZQUYBL8KUkURGw20^0+x9scRCp-O8kckyPW{^Zttt%YyA7~F0cTH)@E>( z{Jk$*AMNRceDfQ7A&XB?%#*Y@_8MlwolfyvZt;={3h$5ZzoeiICwJc;@iMqS!qew{ z3a@$aIb>njh4Ih+VJ-IoeSEambswkh8Z+kzePj3b+bzaPqFIOjnSLBK49A^#gsKd! zQ3E=vSjt;@wr88=!U&y{hZPN4)&;uWbA)NO2ciF*t2Kh8NZMxd0x|#_0v4pzA_4Zi}yW6 z2lke2oC#?T=r6N5H7t}yc@}{XAd!EElfP;v^c5Q##~x?JO)J=Be{M^p(N<`|Kfdhz z|9gah{sK@QKQq?tm(x8alI0AL6CJF=pFmO>ifOs9>Zn)QSKtcu4B%oSN4eP&cakp*xM6l`J4v;Af zJsC+puV5el)52omC;S>(D&5pIG?x$9eZQAZ0X>|yU9dbL{Hxzr*9R=JoLP%qw> z{n;9VuvOMg%i#!$c`$lhxSq{77Y0j|4^96=Po{4-0lLU1f})UdTyIVQZf#vKmvv8d zEX>z(4E;Wj|K`Kl@rj{at5f#S#MW+O7x|oFg{mB{tWE^|nUE@S5qs8xmwx8fN^_Ev z2W`M-TjlRY|E-kaUl?t>L*a{KdzQD?8UP5%+R#D>a>_E;d(3LZYXCS63)F4i@0D%Y zJ1wqr?*kdRwHu`sm5`_b=I_fxR44b9Io0jvnvyy|r^oUC<%?{OwsU7H zxNW=Xmd{8PjaEeRYK}_a&POX%th1Ds;mkL<4j@lD3y=_?qIsj6k=XH%aez@??-r3X zT?S5{o&}P7hVkL})iZ}NryH?G={2~2{=u}s|6<2lQW-Y?Y{7VJ8w>oyAUU@mk8l2m zLdRTm1BCUTulIi#cs+b|Q+v3w>;rqN8_9R%O2gAfHRWBOle+45UVQ_33;N^Gtm@?* ze1{P;xEJJGkGrlGoiaXo^2(>PC(|MMADn_WT9BJ9?KadQFM41|Kq*nz4X^8rHT*Mh zAlauwlSOdTB_0v^`!&QcLr1^*`}Hj@p6Q_fA6H1|Tk5I*xJoB8bj1EXj>v0Csi~U3 zkMru){MuQf!G8?&kYEH?{EvZ7Y3sHI{>MNf|JSb+Sc}=1cN5(4*ERUvg#8Ay{&45N ze%2nE-K1>s!vFpIx72_AY97`a_^;a#ohi-#`2#fb#9z0MB~9T6|GNDWm!-r1*X=!m zIePbBx5bm^i2r)dAmjfHyW{_T1VAqS`y-ef-fqKDy;g_k)incc?lzi*!j_&j$A_;^ z&JtVxB|qN$JQRFWXlqHxT)OOLxO!MvLTlbDib z%#hk}=hFShXg<{K=5u~??8&Vn=YbN5cxsO7rtgVCF3wbk(bQZ*0M)@}u1VEuFu%!& z(P0E_Q3|IMIuI)QOMvfb4;Hnt_SGnbjtfRZff7Wo&>y>{-bJN=&zZ`*>TPj|N@R9D zd!aOD`@PM(%VgVTl*3ou!Yf_10~+_ro;T5&{RFn_3^9XqSBk<>4B&r5J2pdsC)%w~ zIc-iQ)8`Ky^`-F0Y*7rS-A$Ar6}~=U?1$)`I9dd2o;aff|30Bw1n-zgqlZ$L>pBg* zUAWeGZvSkRcohrK;-BUD{*vDLB8_Qnl3R>-XmO|Y2{Cq_*JE?41ZAydV*BkTlEP+_ zMDa_4_+pK($2*IwfAA?)YfQg$&g8YN!GF#3G0F$8NKDzc#-{{pnAm4s-6$S$BH-JE zEfFzbu_ulqftr$cvejX}j(2dF6ekV39Jj6;?$7KgdK$9cnSCic-F-CfnUF5bzbs|U zTim}!?`VYN$W2;YL$zrYc1l5vAPF5!rl;D0o9>Kn5W?39(KM$mZf~t3qB~l#6SWwq z)CunlR6M9P^Pzcry3MzoxIG?65R}>Rmz_^&5O%FsB@6hMG+~b6_JswHRy|V+=d?x3 z#A!V6Z*(HC*SZC&Tr9KY08IwesPhGTD| zjnou*D?ne4W-uL2^mxl$!1rt-7YhTp*bUyo!q*23uS<7GLthm+TCBm|MZx=YZ1hqkP!e_eQav2%w z45@795W_c{n-xP!n){2wR+!0)d{#uh_*X^JiM z^E4f;<*sXD_*E~wTI^*yq-SG!+ng9vgno&K_pNOMqaMyuk9LmJbe-y3h~r z1-|b~|9Vi{Th5(8&+6`sy-jgHUh`FQv3R=fC?1!^+g8$Kq94@W7R}ydX2k0vIuKf6 z;Ltl}B)@fL5AtiE{qU)%6VLzojw_;j*WXvj& z3f|!lH0CU_H5Jld@1(DHE=v@sGo9e|Y#$3W)L6Cm+L;+w+?0YJ!+zdq)ew#@LM?dp z(v)>M-%S~6g>ob*z>}|XEsGHS6Fo`w z%gJ;_FD^Tl+-*NJC@{-Fx@q;k&tw;dZ}BKjth5DRdBECM*i*OzUIV?gDRamSezCVy zB*k!wfF zj+%&+HOWxk-Ut-Q%bDuNd@ft_cqA3U8>Fs29HzBS?4$16%DfI)?eGf=32nPfO zvu}EUwaLbZBO*fEG*qYHXFPbyh1y30C_w+VtZuGP8wtO;;vv3jxD`+H7z2zMF7r|q zaq80>ljN`}xFa${hd%0#v>O1l`+YN@9B7ZXLHGs<^3u!Z5%Ox~>|jv-@T#k}O|Wi9 z^2^OOr%h%uY76+NDh1BCh1n{ftpVQHfc6&CI()@i9#3MBXOMZ`7HIW1SE;RQX&rON zDt#tbGo4BP5c)ATb$idroYV}f(xZ)1+%demPWnLj_Lc?gpjD>$Ks*$WG>?UX%2PX| zS1O%lP+2H%J+7mjDbe@Wj}MQAgJ}E(?LwuC=kC zj;aDwp_f;E2xX-Zs^E(p87U0?+#Kr{IysBhP|9!~f+n%YM*wqBOZ~Z6sqQE%nG{FN z(%YEzKyBzs;yJbRT;KQL)RH>dr41CuvBFU@f`j!K8}rj9OL4m*G6U;ZaC8(srSdb6 zQH`(bbsM1%^n&Tnq}G3wkE+)*VtaA*lN+z&Lm$>|YK5@;cKxO3X&d!Ark{JZZDE4U zX?dQUgH=Lj!ll^TTiZLbD#d;^56{grf9>%8l48cypVC6`Z>`Wm_2%Fg-`3K~!AH@? zH#SqLI&R|kQDZa+)cO#4p&8-MNZ@F~{wT42ftKnvLy+_mv>lC#Ccf+!Ner3HAx= zIK_K#N?=vpm=6OmJCEeT2=q^^66dfi?Jhk4QgtA(YWc_MIQCi`cf4H_$Em=F9Ko155bpR)h;b+*@ zt(JwQ8Z#fBWm0Oq3cXejB~>>~xT@C;sq}{$_>fu=_!YUM#Q=s;&kR_>*#2WXInP5pq}3+HU zUVk{y=usD^v~f47wr`miw9dtCzlAZoaCJ62BhC&BY;c21wYTxc1}m_Fpbju#7ST~F zy5=<-SSr|Wcr(v&!LxUxghB`QfpwLX>(rrby#*tLKWxl8EV2uh>04q|)y)c-Qz4-# z6+Z#7qAt(HdpXkVdUp$g`}#S(@bd1>Uvj$xN!n?KwjQ+Vvnb4$gU4x3zf0gV%2=1n zPtv2zth6$tMmR*dcsVnxgrtO1N42*ViF7_I{d1}By2;dS8S!l=(3H>OMc3MfS-8_j z``NpDtv*)GJ$Y=QXmv-`XT93^sL%EiXI{;{3xnelR*nuUOul_fm7I+FJ~BG3X5OG0 zR>bbHzGJP(Msvf8{3{=ic^n7pUx*h5f1ZNBuo9BCC||vI)nGdJ&qsZ3at>0AS4OvG zjI0yVjNKeLI$`W>;9?PDxi5}#V*~*JBDblFHKS9CRcmVmz50MpUL#%bi-}_!P%7In zqu6=~kg32bR^B1tICZLnOp5h_AB8p^xfzCa<@yX$!g~Fq*5a-R-6-#r7@IMSrf>@= zcJNffl;dh?QX1&8dlMrU`7N8Ma;b)v?UQp%vMR6--d)iJx7j)R_2^_6^_(fZwUvdn zRg%L2Vv_f>;3T9%L9(r~hKO4!@Jrh_Yl#`T&WlGb+&2+{PUm<*J45! z*u@~4Q7hX}Lxt(bVI^EnxRBt*!c?>y?oku>KmS0&cyMN>WIg+2&LgQ?#g!Fwizo-? zpGl(`58Q1rMebff?k@(b!9&?PMVFDIlc1`AT<*4&kUaVz_Oj2A^Tna-BZdxET=tOR zPkI{@`?QpTpPfi4xu@f&)}sauvv_WOEUt|{2ww0owJ-BaC`p(!)&u*RQg38zN}y^z z?X@yvpF%A6S*rq#{( z5vw7NY&cWa3luP$>}3%|rMv+AELGabbwc@5{Og&cZ8UK4pHVDOw)^idVxvea7CR_E zep7#E*dNbJ+l|wqi_cVfg}ihfai5N0)Je?3*eCDuhJx{P_akCkS#j4gcoMetvb04V zA1AqH>kYhg><7Q+2oh>bX?Wg$f$bYUV6Xd2vx=dT!c%aBsh^BGp)~XE&k(=>ey20h zsz-e@A|%GE);ZS>YCdEP5_Ch1BZtayvA$A*?^_cMtKvT!O_6o@xTTrXh3nd9riSzB zs-D;!l>A{rFtGDlcUrE-H~X3x!^!SU!vt$YT-&0}@`wr(swl+KO2MW^Ff6&X?!Bm0S_(3k z_TlxH*h?U`Od9Fvpz#^}P{hT=U|j%WsHcYYG9ZB;lBkZm9g{ldyXg2KpX-#Iwfg=7 z--@-LvU)Ys_ajzXS)I|Hd7U2_bhRFK?J9aD->o3Dz=-O#Wolc|UOxi<)QI;OA&pQw zvoa&M2CC{STsgnK@fULNm$%e}s03VIE8%95;d90C%{-@#Ih6nyC-A74@z;^Q??*vx zQ-OSsWbP*!68VmH6KR<01UauK!R#Z!PX^bN4 z$uGzgtWfpr=Tbllh8Nib2Z58JTDq+4*4FOUg3UVAjPzr2_~@n8ave;Tp5Le75iMW0 z&-4Yqa9&Q6qvC*^bc!c-_FGDF4KbGHVSDt;#>O_-yzv4i6G*-5T{Z=1A+wW6 z-IXhMZd1qCzBJ`e1>-^>sv}pF`N|i=NDS;8BLDmXs3NqzGF@jIZR>5UNQf!hd=a7^rE&ofQdGocMs+agg*s7|k7xUjv*c_!J3oK=VmBQSOrl%p5v;6BE3 zmL7`hGeYi+4kI#HY1T;E$D_Zb;~t9tHXPD0P#2f74|N_S7Yw-nNsCO*KBl}Ar?oD$ zaH8~FNQxcyRA5b4p2jRu?)&tm%w6Sd>G-L;&Bjyn%@e0gJdmdxNZa&H?SbN$p$<+mMe<=Oowa6bMkoB6g7(|DpcAPHn1nY8Kb#}` zp(FJ-G5)oT);>dwHB1eK$Z#aA1f7tdLT{~8m9{AeV*ra+E!>s!t<>>DrnH3jQ+cr? z@vb2E5jhl64^H@$^Io3bLjPozakRCKmUFLQzeHPWCNjM18Av4o(MFj3Dz&*}aiWl| z`!lh(#9d{9iI9sunJ!Jv0I65~TV|X&2P|m93 zcd9iypVTyl6c2VR(FT=;RmIr~Md|Q{h!6m#(BG5qkExnPjzF!9MIb|ze9^WHVpY11 z-|%u}iB~~)-^ReC|8=3AL4L_aZH2EHJ0m;ZQo)aV-}3SX`Z2*pi&dVdp+aYZiC#F? z5zZt|cEf5Tv5hX{RHTEcNHu&4?JVEea7`E)lOgd6dg|Kik`(CSyg@iPOf5|a!0$*X z{8eVSU&xz67K1z%66_=%ZvB^3(6#QhvTjE2`(((9<+D1o_8K_uB4E~rVJUq#vTVI( z->6}>^#c{H>e8SZ@7x$i^uB7dFA3+JFWw#M4(b z8v6hwm|!t#1yPEBMakB|7zL`gR!)V@-f~Kg`U<$-b>{w7k*a?5#a5PXuS(m|)X7>o zjUwb2&E!Q-wQ0t{(6il>2rWe@fc!|eYw+9F;e};01s3=7!?S~SCj#3qnlr$0R&)(Z zgmrBk#Y~W zE=M)I7C+Ljxds6IdmTrnkq-c};O$)6c-xuj(m2bJVTM%OSQse|*;~T4dz{Jdw8?92 zH{7r)E)pLi2gsWx;d`~j`N*6xO}((9twr{1Lww0wpFIIWXU=a8C}dm>8LeFALC;Hc zgLN@?@~0)cd0+@W1g`9!E;Gba7JQ-htiPDZd%xD);!hRhMVlCl z$Cej5n;*TH1cZd*7?l?F23QD)Y`mxMo_jLpNk%nR)s`AW8FxKKr0MNm1Er$8DT@#l zxqzDLe0RU$1^+&8xdZYoiGoWm8=P(`6T zKtyH+WP5ZFa$4ja0ZJ*)l#ds{sX`aX5qV?DkTA;|DTd?L0&y;L`OX9N@f6y+XUj9g zJR-)&G4^fYn*F9X0dKtv3y8Be94tu#P0y?+>yb43E+xhLOnD0hhXOMgaY-2_mdyA{ z5v-5S=M*RswGT?N4MpN+*rDs0 z^kj?>_*0j_PtF(>g-^###g33Qt)xeviA?g^s2hvu{`vwrGW}76_I$viRQ42S{loWM z;%af@Hls8#(L%J~kkA8`4s0Uv#Y3l!tE4EszE zcs&M{J3qavP9%0xWjH|_APJSTUe6|+jy)oEtBAAF#;Uz!YCNVgo7b9Ws2oRclU!jZ z6i!0zoy={kaDgJ#!1gGjW}=O21!SuChu-o>p)}BS_a-V+cZI$X-uqMCfScRsRc<*2 zmwAq?esEsD7qjxEDHjOlU8JRrOhN(Rgjj@iV0_Y!KCi0nFBk}|&l#6@rAAzkyCdNU zjz{~z`GKy{7UCiK)VswKl@Yp$Mn9|?*(PgK6UB6BJ-u+$foPRP%^CG}Qbf%H@9ayu zN2od#%pumTB7Cl%q35#l1F%CvEr8?tBhoZ)IWXjB zb7MGv=VV9~E;foh19ivCljMEmhwVG_Bj6!d;bhJz9OpTr81Op!b{T(uGBeytn!J&1 zZ5BhJSItSIIin-lI)3&@Vdwsuw~GhB!ew(+i(6+LzqYaaH}!ghHNw=u7QGCQmgk$8 zGHhpOBuLA%oL35l3-A54LK?vbvzgdN=GtUDW?xcCe)e11MincY+4_B|u>`uGO(w&i zKbZ2T*MOok_7xZwvTMkDbwvA)rW&{5)=g+PlMM5n<{yaJmpLXVCVY}exaNT}9?>=- zg6Z}dU#Kqm)@W;!U~f6^mZ^9(IifsVK0Ns*%wcmI#0O`6g%GV0sIfEN&mWm*#bXLi zFVG27@yQUxy018V7&zBR{hiTK>$zA$vB-4n<09-rAoy5!-(=uN5;d61P{RdXwAgtc5zvM9n70M~3cuzSzf-%`3&FPhlvdLogJU9i(uv+tHK z+@OnNiiXTcoZL(G6#Rylil4Gpe%5Rq5_W$*tmx*1NpS<$u7;&-J-_bl!{SY4bPGNa;4Mj*Sh>D z54@Z}^(ddsW&fdmAjEvpD&4}t%DW@n8pu}^Kc^v9kdPdENe`eOt8SU~@FMZ+Q%a@= zPV`<$!Ir3IfYJEXQGcK9Kskw=36J&Zou)h>UFabtUcX216zF^Sw=0c2wJp*+?de1kG1jM>Zn zLm40wai`aREJ}~XQ)vMJrbCt1t+bh0KBb7%WKOb+Ph$-1&8^S zJsqcdf;QrT<;se{aUG%R*ZyJyi`ge0O~GC4WNs1yG6J^w+bxweL`FbT)>=U*;@dS) zX`4I40;_fk4u6iI&>VXbDoFsV1+_KZf|v~pG)|e35{44o^T-J!L4C&3;6;L*DXj7E zK+LL%!2|OW;+ZkPh>ffRN^!rV)CkyiXdax{Li)#Nna4p%HQU=uiYQi3BL0?mzux9% zo<&@N++pnqIoTwN^JvU&KY%mMX@BBvJd*Uf$P0IJZ@HZCMzJfZ$Q{|&W(4~bMCUheT- zJzfAUiE>z-d4AaBT_#QnPr>m-;$cle@w0)zfMOtyraCGNu*FR>) zs2e7eP^($Q;+7zIe;4f_cMSRX9<*rqmpbe)!R;>8DZ1x=WK|;}_(hYCZ(z!-<7n(9 zCq+{^jD)mK+*017oU^uyvhqM+X8K)@{_*IV&B8w)a!}ME`zel@kz_@wjvl}yiUJI3 z+bUo)A5MB$qO*dnx~$1@20e(%Yw)na`w>3XXxlbB%ipbHDhgtM$LQIYXye(T@Y;>A z2y!6h8#8k`Y&k;;*S5<3VwllwZ;kg7I^FK#NVR!^AmlILwjH6&pT0xLQY9L|qtH>S zv7u7~jFje>(p#z*Np<8lob09CLi|h; zOAYF|;gtyX9Of~k6M}Fm(O-40HEeG->+~nNhHu$hk1a|4h-A)(*_pbk?EsWhnfL!} zxIbE1%j98lLY>1^3mX;HM;V>+)k$sfnf)l<2t_yc2n1p><Ldv2LwXFsrCVDc?z zwx1stT|0|S`TU?PS+Sn~qFi%bC6W??->ODO1F`I0icf^cNS&y>pLIe#uU3by15x$V z!I^x7rpLE|Z~*=Kt}ute8`~|#dPhhNbzF>QVk6vZW!1r`GPNK)9XE>;y~nAAm||vA z7gVJsI?Df&xl{!IL-=Mz5z0qSnD8{iY-F1$hr-#gsn%b-K)bEwZIy*p8-%(YHa!Tg z;*X&F8+25s?n@z~kloRcAmfWY+-={5zPi!3fqFr);eLWnRi*`U@Brgw9tk}!FF=_5 zBeT-?(|T3?N`Yz{2r#!~ztQ>Kc~L{4?7=rs`S59F3=r*1UGR3Z9|hv{j;tMqgc7Kq zb%0WMy{%w!!-qqwe*nxAhbZ5w6*2v~G}hZU^Z68|6gpZZ3BGVq_qJL0(0TsJ5Wkjs zIihw*LUf*CbUZ15IvNZ(EX}~lQ&pwKw}q#aKv^rBPMMhM|g2}t( zfx_qc@^*jln426{U2cuK;aii|*2XQH%BdyE6YC72>c-)C4})dci83Gy!q7tf&6ivs zFVSdM_hU)mx$#eyY|>4t>&Y3d`9NN}4koyMnLTzGc{Y<>&VEeTKD4O$V+5#nJKaD- zl4ePAfar(@>HW0iM5a4nz{U!Mav^I!8vC2z*F}1(g`&fUrro6=@M6#(pcbk*Y!} zuNuI~n^xIsr=U7XS`;lp%fhTW5=au!NWf0|n<^hr8EL8(0uOd0v+GP+=-yV%&@w9( z170;6ACne63sZN>md!+SfD zsd!L#?T6~fJCq|D>bQEZtJ

  • 5Sq5h#I$|anLwUD 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�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