From 15fe61bde537052050e71a1a64f3ee1d02454f1b Mon Sep 17 00:00:00 2001 From: Mikhail Yasnov Date: Thu, 18 Nov 2021 15:54:47 +0300 Subject: [PATCH 1/5] Add spring workers manager --- README.md | 7 + settings.gradle.kts | 3 + spring-workers-manager-agent/README.md | 77 ++++++++ spring-workers-manager-agent/build.gradle.kts | 16 ++ .../workers/manager/agent/AgentInitializer.kt | 30 ++++ ...tationConfigCollectingBeanPostProcessor.kt | 80 +++++++++ ...ConfigInitialTriggerDescriptorsProvider.kt | 38 ++++ .../AnnotationConfigJobProvider.kt | 14 ++ .../agent/annotation_config/DefaultTrigger.kt | 15 ++ .../annotation_config/ScheduledAction.kt | 10 ++ .../job_factory/AnnotationConfigJobFactory.kt | 19 ++ ...heduledActionAnnotationConfigJobFactory.kt | 51 ++++++ .../AnnotationConfigTriggerFactory.kt | 14 ++ .../TriggerAnnotationConfigTriggerFactory.kt | 47 +++++ .../workers/manager/agent/base/BaseJob.kt | 9 + .../agent/config/EnableWorkersManager.kt | 10 ++ .../manager/agent/config/WorkerInitializer.kt | 37 ++++ .../agent/executors/WorkerActionExecutor.kt | 13 ++ .../impl/WorkerActionExecutorImpl.kt | 71 ++++++++ .../manager/agent/quartz/RunnableJob.kt | 32 ++++ .../agent/registry/JobDefinitionsRegistry.kt | 64 +++++++ .../manager/agent/registry/JobProvider.kt | 7 + .../agent/registry/SimpleJobProvider.kt | 16 ++ .../agent/registry/TriggersRegistry.kt | 37 ++++ .../agent/scheduled/WorkerManagerWatcher.kt | 67 +++++++ .../agent/services/SchedulerService.kt | 10 ++ .../services/TriggerDescriptorService.kt | 12 ++ .../manager/agent/services/WorkerService.kt | 12 ++ .../services/impl/SchedulerServiceImpl.kt | 86 +++++++++ .../impl/TriggerDescriptorServiceImpl.kt | 43 +++++ .../agent/services/impl/WorkerServiceImpl.kt | 36 ++++ .../InitialTriggerDescriptorsProvider.kt | 15 ++ .../NoInitialTriggersDescriptorsProvider.kt | 20 +++ .../workers/manager/agent/utils/Glob.kt | 54 ++++++ .../agent/config/WorkerInitializerTest.kt | 63 +++++++ .../scheduled/WorkerManagerWatcherTest.kt | 111 ++++++++++++ .../workers/manager/agent/utils/GlobTest.kt | 32 ++++ .../workers/manager/utils/MockitoUtils.kt | 17 ++ spring-workers-manager-api/build.gradle.kts | 14 ++ .../api/controllers/TriggerController.kt | 41 +++++ .../api/controllers/WorkerController.kt | 65 +++++++ .../manager/api/dto/TriggerChangeRequest.kt | 21 +++ .../workers/manager/api/dto/WorkerDto.kt | 21 +++ .../services/TriggerDescriptorApiService.kt | 10 ++ .../manager/api/services/WorkerApiService.kt | 10 ++ .../impl/TriggerDescriptorApiServiceImpl.kt | 50 ++++++ .../api/services/impl/WorkerApiServiceImpl.kt | 22 +++ spring-workers-manager-core/README.md | 1 + spring-workers-manager-core/build.gradle.kts | 13 ++ .../manager/WorkersManagerConfiguration.kt | 37 ++++ .../manager/core/config/LiquibaseRunner.kt | 34 ++++ .../workers/manager/core/models/Execution.kt | 51 ++++++ .../manager/core/models/TriggerDescriptor.kt | 75 ++++++++ .../workers/manager/core/models/Worker.kt | 48 +++++ .../manager/core/models/base/BaseEntity.kt | 24 +++ .../core/models/enums/ExecutionStatus.kt | 8 + .../manager/core/models/enums/TriggerType.kt | 47 +++++ .../manager/core/models/enums/WorkerStatus.kt | 8 + .../core/repositories/ExecutionRepository.kt | 6 + .../core/repositories/RepositoryUtils.kt | 20 +++ .../TriggerDescriptorRepository.kt | 19 ++ .../core/repositories/WorkerRepository.kt | 34 ++++ .../manager/core/services/ExecutionService.kt | 12 ++ .../services/TriggerDescriptorCoreService.kt | 11 ++ .../core/services/WorkerCoreService.kt | 13 ++ .../core/services/WorkersStateService.kt | 10 ++ .../services/impl/ExecutionServiceImpl.kt | 34 ++++ .../impl/TriggerDescriptorCoreServiceImpl.kt | 32 ++++ .../services/impl/WorkerCoreServiceImpl.kt | 25 +++ .../services/impl/WorkerStateServiceImpl.kt | 52 ++++++ .../db/changelog/db.changelog-master.yaml | 165 ++++++++++++++++++ 71 files changed, 2328 insertions(+) create mode 100644 spring-workers-manager-agent/README.md create mode 100644 spring-workers-manager-agent/build.gradle.kts create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/ScheduledAction.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/EnableWorkersManager.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt create mode 100644 spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt create mode 100644 spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt create mode 100644 spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt create mode 100644 spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/utils/MockitoUtils.kt create mode 100644 spring-workers-manager-api/build.gradle.kts create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt create mode 100644 spring-workers-manager-core/README.md create mode 100644 spring-workers-manager-core/build.gradle.kts create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/config/LiquibaseRunner.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml diff --git a/README.md b/README.md index dacfb12..6908fca 100644 --- a/README.md +++ b/README.md @@ -199,3 +199,10 @@ Disables Spring OAuth2 resource server for testing. ## s3-storage Amazon S3 support. + +# spring-workers-agent + +Framework for clustered scheduling based on Quartz and Spring Data. +Stores all data in a database (Postgres by default). + +Used as manageable alternative for Spring's `@Scheduled`. diff --git a/settings.gradle.kts b/settings.gradle.kts index 1887c54..afa55c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,3 +51,6 @@ include("security-resource-server-custom-jwt-configuration") include("security-resource-server-test-jwt-configuration") include("security-jwt-common") include("s3-storage") +include("spring-workers-manager-core") +include("spring-workers-manager-agent") +include("spring-workers-manager-api") diff --git a/spring-workers-manager-agent/README.md b/spring-workers-manager-agent/README.md new file mode 100644 index 0000000..bc18f65 --- /dev/null +++ b/spring-workers-manager-agent/README.md @@ -0,0 +1,77 @@ +# Workers Agent + +Framework for clustered scheduling based on Quartz and Spring Data. +Stores all data in a database (Postgres by default). + +Used as manageable alternative for Spring's `@Scheduled`. + +## How to +1. Add dependency + ``` + implementation project(":spring-workers-manager-agent") + ``` +1. Define a job by annotations to be scheduled in any component. + ``` + package ru.touchin + + @Component + class MyJob { + + @ScheduledAction + @DefaultTrigger(type = "CRON", expression = "0 15 * * * ?") + fun sayHello(){ + println("Hello, world!") + } + + } + ``` + +1. Enable job in `application.properties` + + ``` + workers.names=ru.touchin.MyJob + ``` + or: + ``` + workers.names=* + ``` + +1. Start the application. + +## Annotations +### @ScheduledAction +Registers method as action of some job. + +Parameters: +- `name` - name of job. Defaults to class full name. + Must be unique in application scope. + +### @Trigger +Declares default trigger for the job. +Default triggers are created when launching job first time. + +Parameters: +- `name` - Optional name for trigger. Defaults to some (maybe random) string. + Name must be unique in scope of corresponding job. +- `type` - Trigger type. See triggers types. + SpEL expressions are supported like in `@Scheduled` annotation of Spring. +- `expression` - The value for trigger. + SpEL expressions are supported like in `@Scheduled` annotation of Spring. + +## Configuration +### Enabling workers + +Agent ignores workers by default. To enable worker add its name to `worker.names` property. +Example: +``` +worker.names=com.eample.Job1,\ + com.example.Job2 +``` + +#### Patterns for names +`workers.names` support Glob-like patterns. +- Asterisk (`*`) symbol is for "zero or more any symbols" (as `.*` in regex) +- Question mark (`?`) is for "any single symbol" (as `.` in regex) + +## TODO +- External data source, unrelated to application code. diff --git a/spring-workers-manager-agent/build.gradle.kts b/spring-workers-manager-agent/build.gradle.kts new file mode 100644 index 0000000..1489e80 --- /dev/null +++ b/spring-workers-manager-agent/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation(project(":spring-workers-manager-core")) + + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + implementation("org.springframework.data:spring-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-quartz") + + testImplementation("org.springframework.boot:spring-boot-starter-test") +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt new file mode 100644 index 0000000..7f1d2da --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt @@ -0,0 +1,30 @@ +package ru.touchin.spring.workers.manager.agent + +import org.springframework.boot.context.event.ApplicationStartedEvent +import org.springframework.context.event.EventListener +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.config.WorkerInitializer +import ru.touchin.spring.workers.manager.agent.scheduled.WorkerManagerWatcher +import ru.touchin.spring.workers.manager.core.config.LiquibaseRunner + +/** + * Prepares required resources and initializes agent. + */ +@Component +class AgentInitializer( + private val liquibase: LiquibaseRunner, + private val workerInitializer: WorkerInitializer, + private val workerWatcher: WorkerManagerWatcher +) { + + @EventListener(value = [ApplicationStartedEvent::class]) + @Order(Ordered.HIGHEST_PRECEDENCE + 500) // +500 is for "higher than any normal, but lower than any highest" + fun execute() { + liquibase.run() + workerInitializer.init() + workerWatcher.init() + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt new file mode 100644 index 0000000..16ca5b0 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt @@ -0,0 +1,80 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config + +import org.springframework.beans.factory.config.BeanPostProcessor +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.annotation_config.job_factory.AnnotationConfigJobFactory +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import java.lang.reflect.Method + +/** + * 1. Scans components for [ScheduledAction] annotation + * 2. Keeps metadata of that components + * 3. Creates [BaseJob] for methods created + */ +@Component +class AnnotationConfigCollectingBeanPostProcessor( + private val jobFactories: List +) : BeanPostProcessor { + + val jobs: MutableList = ArrayList() + + val jobName2Method: MutableMap = HashMap() + + /** + * Bean name -> class of this bean. + * + * Contains only entries for classes with [ScheduledAction] annotation. + */ + private val beanName2OriginalClass: MutableMap> = HashMap() + + override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? { + val hasMethodsForScheduling = bean.javaClass.declaredMethods + .any { it.isAnnotationPresent(ScheduledAction::class.java) } + + if (hasMethodsForScheduling) { + beanName2OriginalClass[beanName] = bean.javaClass + } + + return bean + } + + override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? { + val clazz = beanName2OriginalClass[beanName] + ?: return bean + + val actionMethod = findActionMethod(clazz) + + val createdJobs = jobFactories.flatMap { it.create(bean, actionMethod) } + + createdJobs.forEach { + jobName2Method[it.getName()] = actionMethod + } + + jobs.addAll(createdJobs) + + return bean + } + + companion object { + + private fun findActionMethod(clazz: Class<*>): Method { + return clazz.declaredMethods + .filter { it.isAnnotationPresent(ScheduledAction::class.java) } + .also { annotatedMethods -> + check(annotatedMethods.size <= 1) { + "Class `${clazz.name}` has more that one methods with annotation @Scheduled. " + + "Methods: $annotatedMethods" + } + } + .onEach { annotatedMethod -> + check(annotatedMethod.parameters.isEmpty()) { + "Method ${clazz.name}:${annotatedMethod.name}' must not have arguments for scheduling, " + + "but requires ${annotatedMethod.parameters.size} parameters" + } + } + .single() + } + + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt new file mode 100644 index 0000000..9d68af9 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt @@ -0,0 +1,38 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config + +import org.springframework.stereotype.Component +import org.springframework.util.LinkedMultiValueMap +import org.springframework.util.MultiValueMap +import ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factory.AnnotationConfigTriggerFactory +import ru.touchin.spring.workers.manager.agent.triggers.InitialTriggerDescriptorsProvider +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker + +@Component +class AnnotationConfigInitialTriggerDescriptorsProvider( + private val triggersCollector: AnnotationConfigCollectingBeanPostProcessor, + private val triggerFactories: List +) : InitialTriggerDescriptorsProvider { + + val jobName2Triggers: MultiValueMap = LinkedMultiValueMap() + + override fun applicableFor(worker: Worker): Boolean { + val actionMethod = triggersCollector.jobName2Method[worker.workerName] + ?: return false + + val triggers = triggerFactories.flatMap { it.create(worker, actionMethod) } + + if (triggers.isEmpty()) { + return false + } + + jobName2Triggers.addAll(worker.workerName, triggers) + + return true + } + + override fun createInitialTriggerDescriptors(worker: Worker): List { + return jobName2Triggers[worker.workerName].orEmpty() + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt new file mode 100644 index 0000000..8f7d311 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config + +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.registry.JobProvider + +@Component +class AnnotationConfigJobProvider( + private val jobsCollector: AnnotationConfigCollectingBeanPostProcessor +) : JobProvider { + + override fun getJobs(): List = jobsCollector.jobs + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt new file mode 100644 index 0000000..059f6b4 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt @@ -0,0 +1,15 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config + +import java.lang.annotation.Inherited + +/** + * Adds default trigger to [ScheduledAction]. + * Default trigger is submitted if job is launched first time. + */ +@Inherited +@Target(AnnotationTarget.FUNCTION) +annotation class DefaultTrigger( + val name: String = "", + val type: String, + val expression: String +) diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/ScheduledAction.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/ScheduledAction.kt new file mode 100644 index 0000000..28f4c29 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/ScheduledAction.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config + +@Target(AnnotationTarget.FUNCTION) +annotation class ScheduledAction( + /** + * Job name. Defaults to class name. + */ + val name: String = "" +) + diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt new file mode 100644 index 0000000..0ef1358 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt @@ -0,0 +1,19 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config.job_factory + +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import java.lang.reflect.Method + +/** + * Invoked when found method with [ScheduledAction] annotation in some bean. + * + * Used to read jobs settings from annotations in components. + */ +interface AnnotationConfigJobFactory { + + /** + * Warning: As Spring could substitute actual beans with proxy-objects, + * you must carefully check if [actionMethod] is applicable for [bean]. + */ + fun create(bean: Any, actionMethod: Method): List + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt new file mode 100644 index 0000000..ac7585d --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt @@ -0,0 +1,51 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config.job_factory + +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.annotation_config.ScheduledAction +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import java.lang.reflect.Method + +/** + * Creates job instances for every annotated action method. + */ +@Component +class ScheduledActionAnnotationConfigJobFactory + : AnnotationConfigJobFactory { + + override fun create(bean: Any, actionMethod: Method): List { + val job = createJobForBean(bean, actionMethod) + + return listOf(job) + } + + companion object { + + private fun createJobForBean(bean: Any, annotatedMethod: Method): BaseJob { + val targetMethod = bean.javaClass.getMethod(annotatedMethod) + val annotation = annotatedMethod.getAnnotation(ScheduledAction::class.java) + + val jobName: String = annotation.name.takeIf { it.isNotBlank() } + ?: annotatedMethod.declaringClass.name + + return createJob(jobName) { targetMethod.invoke(bean) } + } + + private fun Class<*>.getMethod(sampleMethod: Method): Method { + return getMethod(sampleMethod.name, *sampleMethod.parameterTypes) + .apply { isAccessible = true } + } + + private fun createJob(jobName: String, func: () -> Unit): BaseJob = object : BaseJob { + + override fun getName() = jobName + + override fun run() { + func.invoke() + } + + } + + } + +} + diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt new file mode 100644 index 0000000..6ff387b --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factory + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import java.lang.reflect.Method + +/** + * Used to create initial triggers for new workers + */ +interface AnnotationConfigTriggerFactory { + + fun create(worker: Worker, actionMethod: Method): List + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt new file mode 100644 index 0000000..e503845 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt @@ -0,0 +1,47 @@ +package ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factory + +import org.springframework.context.EmbeddedValueResolverAware +import org.springframework.stereotype.Component +import org.springframework.util.StringValueResolver +import ru.touchin.spring.workers.manager.agent.annotation_config.DefaultTrigger +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import java.lang.reflect.Method + +/** + * Creates triggers for methods annotated with [ru.touchin.spring.workers.manager.agent.annotation_config.DefaultTrigger] annotation + */ +@Component +class TriggerAnnotationConfigTriggerFactory + : AnnotationConfigTriggerFactory, + EmbeddedValueResolverAware { + + lateinit var valueResolver: StringValueResolver + + override fun setEmbeddedValueResolver(resolver: StringValueResolver) { + valueResolver = resolver + } + + override fun create(worker: Worker, actionMethod: Method): List { + val triggerAnnotation = actionMethod.getAnnotation(DefaultTrigger::class.java) + ?: return emptyList() + + val trigger = TriggerDescriptor().also { + it.worker = worker + + val resolvedType = valueResolver.resolveStringValue(triggerAnnotation.type) + + it.type = TriggerType.find(resolvedType) + ?: throw IllegalArgumentException("Trigger type for name $resolvedType dies not exist") + + it.expression = valueResolver.resolveStringValue(triggerAnnotation.expression) + ?: throw NullPointerException("Trigger for worker '${worker.workerName}' has null expression") + + it.triggerName = "${it.type.name}_${it.expression.replace(" ", "_")}" + } + + return listOf(trigger) + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt new file mode 100644 index 0000000..a010c85 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt @@ -0,0 +1,9 @@ +package ru.touchin.spring.workers.manager.agent.base + +interface BaseJob { + + fun run() + + fun getName(): String = this::class.java.name + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/EnableWorkersManager.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/EnableWorkersManager.kt new file mode 100644 index 0000000..cbc6f34 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/EnableWorkersManager.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.agent.config + +import org.springframework.context.annotation.Import +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration + +/** + * Annotation to enable Workers Manager module in Spring components via annotations. + */ +@Import(WorkersManagerConfiguration::class) +annotation class EnableWorkersManager diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt new file mode 100644 index 0000000..f0868f3 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt @@ -0,0 +1,37 @@ +package ru.touchin.spring.workers.manager.agent.config + +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService +import ru.touchin.spring.workers.manager.agent.services.WorkerService +import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry + +@Component +class WorkerInitializer( + private val triggerDescriptorAgentService: TriggerDescriptorService, + private val jobDefinitionsRegistry: JobDefinitionsRegistry, + private val workerService: WorkerService +) { + + @Transactional + fun init() { + initWorkers(jobDefinitionsRegistry.jobNames) + } + + private fun initWorkers(jobNames: Set) { + jobNames.forEach(this::getOrCreateWorkerWithTriggers) + } + + private fun getOrCreateWorkerWithTriggers(name: String) { + workerService.getWithLock(name) + ?.let(workerService::unsetStopped) + ?: createWorkerWithTriggers(name) + } + + private fun createWorkerWithTriggers(name: String) { + val worker = workerService.create(name) + + triggerDescriptorAgentService.createDefaultTriggerDescriptors(worker) + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt new file mode 100644 index 0000000..7422210 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt @@ -0,0 +1,13 @@ +package ru.touchin.spring.workers.manager.agent.executors + +import org.quartz.Trigger +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.core.models.Execution + +interface WorkerActionExecutor { + + fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? + fun finishWorkerProcessing(job: BaseJob) + fun executeJobAction(job: BaseJob, currentTrigger: Trigger) + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt new file mode 100644 index 0000000..b71a3a7 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt @@ -0,0 +1,71 @@ +package ru.touchin.spring.workers.manager.agent.executors.impl + +import org.quartz.Trigger +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.executors.WorkerActionExecutor +import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.core.models.Execution +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.services.ExecutionService +import ru.touchin.spring.workers.manager.core.services.WorkerCoreService + +@Service +class WorkerActionExecutorImpl( + private val executionService: ExecutionService, + private val workerCoreService: WorkerCoreService, + private val triggersRegistry: TriggersRegistry +): WorkerActionExecutor { + + @Autowired + lateinit var workerActionExecutor: WorkerActionExecutor + + override fun executeJobAction(job: BaseJob, currentTrigger: Trigger) { + val execution = workerActionExecutor.prepareExecution(job, currentTrigger) + + execution?.let { + job.run() + + executionService.finishExecution(execution) + } + + workerActionExecutor.finishWorkerProcessing(job) + } + + @Transactional + override fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? { + val currentWorker = workerCoreService.getByNameWithLock(job.getName()) + + return currentWorker + ?.takeIf { !it.isStopped() } + ?.let { worker -> + val currentTriggerDescriptor = triggersRegistry.getDescriptorByTrigger(currentTrigger) + + currentTriggerDescriptor?.let { + setWorkerStatus(worker, WorkerStatus.PROCESSING) + + executionService.createExecution(worker, currentTriggerDescriptor) + } + } + } + + @Transactional + override fun finishWorkerProcessing(job: BaseJob) { + val currentWorker = workerCoreService.getByNameWithLock(job.getName()) + ?: return + + setWorkerStatus(currentWorker, WorkerStatus.IDLE) + } + + private fun setWorkerStatus(worker: Worker, status: WorkerStatus) { + workerCoreService.save( + worker.also { + it.status = status + } + ) + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt new file mode 100644 index 0000000..6d2c393 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt @@ -0,0 +1,32 @@ +package ru.touchin.spring.workers.manager.agent.quartz + +import org.quartz.Job +import org.quartz.JobBuilder +import org.quartz.JobDataMap +import org.quartz.JobExecutionContext +import org.quartz.JobExecutionException + +typealias JobFunction = (JobExecutionContext) -> Unit + +class RunnableJob : Job { + override fun execute(context: JobExecutionContext) { + try { + @Suppress("UNCHECKED_CAST") + (context.jobDetail.jobDataMap[ACTION] as JobFunction).also { action -> + action.invoke(context) + } + } catch (e: Exception) { + throw JobExecutionException(e) + } + } + + companion object { + private const val ACTION = "ACTION" + + fun initJobBuilder(action: JobFunction): JobBuilder { + return JobBuilder + .newJob(RunnableJob::class.java) + .usingJobData(JobDataMap(mapOf(ACTION to action))) + } + } +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt new file mode 100644 index 0000000..9c8eca5 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt @@ -0,0 +1,64 @@ +package ru.touchin.spring.workers.manager.agent.registry + +import org.quartz.JobDetail +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import org.springframework.util.LinkedMultiValueMap +import ru.touchin.spring.workers.manager.agent.executors.WorkerActionExecutor +import ru.touchin.spring.workers.manager.agent.quartz.RunnableJob +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.utils.Glob + +@Component +class JobDefinitionsRegistry( + @Value("#{'\${workers.names}'.split(',')}") + workersNamesPatterns: Set, + providers: List, + private val workerActionExecutor: WorkerActionExecutor +) { + + final val jobs: Map + + private final val jobDetails: Map + + final val jobNames: Set + + init { + val allJobs = providers.flatMap { it.getJobs() } + + val name2jobsList = LinkedMultiValueMap() + + allJobs.forEach { job -> + name2jobsList.add(job.getName(), job) + } + + name2jobsList.forEach { (name, jobs) -> + check(jobs.size <= 1) { "There are ${jobs.size} jobs with name '$name'. Job names must be unique" } + } + + val name2job = name2jobsList + .toSingleValueMap() + .filterKeys { name -> workersNamesPatterns.any { pattern -> Glob.matches(name, pattern) } } + + jobNames = name2job.keys + + jobs = name2job + + jobDetails = name2job + .mapValues { (_, job) -> + createJobDetail(job) + } + } + + fun getJobDetail(jobName: String): JobDetail? = jobDetails[jobName] + + private fun createJobDetail(job: BaseJob): JobDetail { + return RunnableJob + .initJobBuilder { context -> + workerActionExecutor.executeJobAction(job, context.trigger) + } + .withIdentity(job.getName()) + .build() + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt new file mode 100644 index 0000000..887ab78 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt @@ -0,0 +1,7 @@ +package ru.touchin.spring.workers.manager.agent.registry + +import ru.touchin.spring.workers.manager.agent.base.BaseJob + +interface JobProvider { + fun getJobs(): List +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt new file mode 100644 index 0000000..a53c3fe --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt @@ -0,0 +1,16 @@ +package ru.touchin.spring.workers.manager.agent.registry + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.registry.JobProvider + +@Component +class SimpleJobProvider : JobProvider { + + @Autowired(required = false) + var jobBeans: List = emptyList() + + override fun getJobs(): List = jobBeans + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt new file mode 100644 index 0000000..f4a96c5 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt @@ -0,0 +1,37 @@ +package ru.touchin.spring.workers.manager.agent.registry + +import org.quartz.Trigger +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import java.util.concurrent.ConcurrentHashMap + +@Component +class TriggersRegistry { + + // Concurrent impl to prevent java.util.ConcurrentModificationException + private val descriptors2triggers: MutableMap = ConcurrentHashMap() + + fun getTriggerByDescriptor(descriptor: TriggerDescriptor): Trigger? { + return descriptors2triggers[descriptor] + } + + fun getDescriptorByTrigger(trigger: Trigger): TriggerDescriptor? { + return descriptors2triggers + .entries + .firstOrNull { it.value == trigger } + ?.key + } + + fun getDescriptors(): List { + return descriptors2triggers.keys.toList() + } + + fun putTrigger(descriptor: TriggerDescriptor, trigger: Trigger) { + descriptors2triggers[descriptor] = trigger + } + + fun remove(triggerDescriptors: List) { + return triggerDescriptors.forEach { descriptors2triggers.remove(it) } + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt new file mode 100644 index 0000000..63cc5a8 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt @@ -0,0 +1,67 @@ +package ru.touchin.spring.workers.manager.agent.scheduled + +import org.quartz.Scheduler +import org.quartz.SimpleScheduleBuilder +import org.quartz.TriggerBuilder +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.agent.quartz.RunnableJob +import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.agent.services.SchedulerService +import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry +import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService + +/** + * Class is proceeding regular synchronisation trigger descriptors from db and quartz scheduled triggers + */ +@Component +class WorkerManagerWatcher( + @Value("\${workers.watcher.sync.interval}") + private val watcherSyncInterval: Long, + private val jobDefinitionsRegistry: JobDefinitionsRegistry, + private val scheduleTriggerService: SchedulerService, + private val triggersRegistry: TriggersRegistry, + private val triggerDescriptorCoreService: TriggerDescriptorCoreService, + private val quartzScheduler: Scheduler +) { + + fun init() { + val systemJobDetail = RunnableJob + .initJobBuilder { sync() } + .withIdentity(SYSTEM_JOB_NAME, SYSTEM_JOB_GROUP) + .build() + + val systemTrigger = TriggerBuilder.newTrigger() + .forJob(systemJobDetail) + .withIdentity("${SYSTEM_JOB_NAME}_trigger") + .withSchedule(SimpleScheduleBuilder + .simpleSchedule() + .repeatForever() + .withIntervalInMilliseconds(watcherSyncInterval) + ) + .build() + + quartzScheduler.scheduleJob(systemJobDetail, systemTrigger) + } + + fun sync() { + val currentTriggerDescriptors = triggersRegistry.getDescriptors() + + val actualTriggerDescriptors = jobDefinitionsRegistry.jobs + .flatMap { (jobName, _) -> triggerDescriptorCoreService.getByWorkerName(jobName) } + .filter { !it.isDisabled() } + + val deletedTriggerDescriptors = currentTriggerDescriptors - actualTriggerDescriptors + scheduleTriggerService.unscheduleTriggers(deletedTriggerDescriptors) + triggersRegistry.remove(deletedTriggerDescriptors) + + val newTriggerDescriptors = actualTriggerDescriptors - currentTriggerDescriptors + scheduleTriggerService.scheduleTriggers(newTriggerDescriptors) + } + + companion object { + private const val SYSTEM_JOB_GROUP = "SYSTEM" + private const val SYSTEM_JOB_NAME = "system_worker_check" + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt new file mode 100644 index 0000000..bc45d32 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.agent.services + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor + +interface SchedulerService { + + fun scheduleTriggers(triggerDescriptors: List) + fun unscheduleTriggers(triggerDescriptors: List) + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt new file mode 100644 index 0000000..f1a4d94 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.agent.services + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import java.time.ZonedDateTime + +interface TriggerDescriptorService { + + fun createDefaultTriggerDescriptors(worker: Worker): List + fun setTriggerDescriptorDisable(triggerDescriptor: TriggerDescriptor, disabledAt: ZonedDateTime?) + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt new file mode 100644 index 0000000..e2aab84 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.agent.services + +import ru.touchin.spring.workers.manager.core.models.Worker + +interface WorkerService { + + fun get(name: String): Worker? + fun getWithLock(name: String): Worker? + fun create(name: String): Worker + fun unsetStopped(worker: Worker): Worker + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt new file mode 100644 index 0000000..651a226 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt @@ -0,0 +1,86 @@ +package ru.touchin.spring.workers.manager.agent.services.impl + +import org.quartz.CronScheduleBuilder +import org.quartz.JobDetail +import org.quartz.ScheduleBuilder +import org.quartz.Scheduler +import org.quartz.SimpleScheduleBuilder +import org.quartz.Trigger +import org.quartz.TriggerBuilder +import org.springframework.stereotype.Service +import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry +import ru.touchin.spring.workers.manager.agent.services.SchedulerService +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.enums.TriggerType + +@Service +class SchedulerServiceImpl( + private val quartzScheduler: Scheduler, + private val jobDefinitionsRegistry: JobDefinitionsRegistry, + private val triggersRegistry: TriggersRegistry +) : SchedulerService { + + override fun scheduleTriggers( + triggerDescriptors: List + ) { + triggerDescriptors.forEach(this::scheduleTrigger) + } + + override fun unscheduleTriggers( + triggerDescriptors: List + ) { + triggerDescriptors.forEach { descriptor -> + val trigger = triggersRegistry.getTriggerByDescriptor(descriptor) + + if (trigger != null) { + quartzScheduler.unscheduleJob(trigger.key) + } + } + } + + private fun scheduleTrigger(descriptor: TriggerDescriptor) { + val jobDetail = jobDefinitionsRegistry.getJobDetail(descriptor.worker.workerName) + + if (jobDetail != null) { + val trigger = createTrigger(jobDetail, descriptor) + + triggersRegistry.putTrigger(descriptor, trigger) + + if (!quartzScheduler.checkExists(trigger.key)) { + if (quartzScheduler.checkExists(jobDetail.key)) { + quartzScheduler.scheduleJob(trigger) + } else { + quartzScheduler.scheduleJob(jobDetail, trigger) + } + } + } + } + + private fun createTrigger(job: JobDetail, triggerDescriptor: TriggerDescriptor): Trigger { + return TriggerBuilder.newTrigger().forJob(job) + .withIdentity(createTriggerName(job, triggerDescriptor)) + .withSchedule(getScheduleBuilder(triggerDescriptor)) + .build() + } + + private fun createTriggerName(job: JobDetail, triggerDescriptor: TriggerDescriptor) = + "${job.key.name}_${triggerDescriptor.id}_trigger" + + private fun getScheduleBuilder(triggerDescriptor: TriggerDescriptor): ScheduleBuilder { + return when (triggerDescriptor.type) { + TriggerType.CRON -> cronSchedule(triggerDescriptor.expression) + TriggerType.FIXED_RATE -> fixedRateSchedule(triggerDescriptor.expression.toLong()) + TriggerType.FIXED_DELAY -> fixedRateSchedule(triggerDescriptor.expression.toLong()) + } + } + + private fun fixedRateSchedule(interval: Long): SimpleScheduleBuilder { + return SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInMilliseconds(interval) + } + + private fun cronSchedule(expression: String): CronScheduleBuilder { + return CronScheduleBuilder.cronSchedule(expression) + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt new file mode 100644 index 0000000..6b8a06b --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt @@ -0,0 +1,43 @@ +package ru.touchin.spring.workers.manager.agent.services.impl + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService +import ru.touchin.spring.workers.manager.agent.triggers.InitialTriggerDescriptorsProvider +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.repositories.TriggerDescriptorRepository +import ru.touchin.spring.workers.manager.core.repositories.upsert +import java.time.ZonedDateTime + +@Service +class TriggerDescriptorServiceImpl( + private val triggerDescriptorRepository: TriggerDescriptorRepository, + private val initialTriggerDescriptorProviders: List +) : TriggerDescriptorService { + + @Transactional + override fun createDefaultTriggerDescriptors(worker: Worker): List { + return triggerDescriptorRepository.saveAll( + initialTriggerDescriptorProviders + .filter { it.applicableFor(worker) } + .flatMap { it.createInitialTriggerDescriptors(worker) } + ) + } + + @Transactional + override fun setTriggerDescriptorDisable( + triggerDescriptor: TriggerDescriptor, + disabledAt: ZonedDateTime? + ) { + triggerDescriptorRepository.upsert(triggerDescriptor) { + it.disabledAt = disabledAt + } + } + + private fun defaultTriggerName(descriptor: TriggerDescriptor): String{ + return "${descriptor.type}_${descriptor.expression.replace(" ", "_")}" + + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt new file mode 100644 index 0000000..fac2ac7 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt @@ -0,0 +1,36 @@ +package ru.touchin.spring.workers.manager.agent.services.impl + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.services.WorkerService +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository +import ru.touchin.spring.workers.manager.core.repositories.upsert + +@Service +class WorkerServiceImpl( + private val workerRepository: WorkerRepository +) : WorkerService { + + @Transactional + override fun getWithLock(name: String): Worker? = workerRepository.findWithLock(name) + + override fun get(name: String): Worker? = workerRepository.find(name) + + override fun create(name: String): Worker { + return workerRepository.upsert(Worker()) { worker -> + worker.workerName = name + worker.status = WorkerStatus.IDLE + } + } + + override fun unsetStopped(worker: Worker): Worker { + return workerRepository.upsert(worker) { startedWorker -> + startedWorker.stoppedAt = null + } + } + + + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt new file mode 100644 index 0000000..e0fe37c --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt @@ -0,0 +1,15 @@ +package ru.touchin.spring.workers.manager.agent.triggers + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker + +/** + * When the agent is starting first time, then initial (default) triggers should be created for new workers. + */ +interface InitialTriggerDescriptorsProvider { + + fun applicableFor(worker: Worker): Boolean + + fun createInitialTriggerDescriptors(worker: Worker): List + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt new file mode 100644 index 0000000..befb24f --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt @@ -0,0 +1,20 @@ +package ru.touchin.spring.workers.manager.agent.triggers + +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker + +/** + * When no triggers could be created by any provider, then no triggers created. + */ +@Component +@Order(Ordered.LOWEST_PRECEDENCE) +class NoInitialTriggersDescriptorsProvider : InitialTriggerDescriptorsProvider { + + override fun applicableFor(worker: Worker): Boolean = true + + override fun createInitialTriggerDescriptors(worker: Worker): List = emptyList() + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt new file mode 100644 index 0000000..eceea12 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt @@ -0,0 +1,54 @@ +package ru.touchin.spring.workers.manager.agent.utils + +/** + * Glob is simple replacement for Regex. + * It uses only two special chars: * (any substring) and ? (any char) + * This implementation has no support for characters escaping. + * Glob has no capturing features. + */ +object Glob { + + private const val ANY_SUBSTRING_SYMBOL = '*' + + private const val ANY_CHAR_SYMBOL = '?' + + /** + * Example: "I love patterns" matches "I * pa?t?er?s" + * + * Based on [StackOverflow answer](https://stackoverflow.com/a/3687031) + */ + fun matches(text: String, pattern: String): Boolean { + val starPosition = pattern.indexOf(ANY_SUBSTRING_SYMBOL) + + val headPattern: String = if (starPosition == -1) pattern else pattern.substring(0, starPosition) + + if (headPattern.length > text.length) { + return false + } + + // handle the part up to the first * + for (i in headPattern.indices) { + if ( + headPattern[i] != ANY_CHAR_SYMBOL + && headPattern[i] != text[i] + ) { + return false + } + } + + if (starPosition == -1) { + return headPattern.length == text.length + } + + val tailPattern: String = pattern.substring(starPosition + 1) + + for (i in headPattern.length..text.length) { + if (matches(text.substring(i), tailPattern)) { + return true + } + } + + return false + } + +} diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt new file mode 100644 index 0000000..b938860 --- /dev/null +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt @@ -0,0 +1,63 @@ +package ru.touchin.spring.workers.manager.agent.config + +import org.junit.Test +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import ru.touchin.spring.workers.manager.agent.executors.impl.WorkerActionExecutorImpl +import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService +import ru.touchin.spring.workers.manager.agent.services.WorkerService +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.utils.MockitoUtils.anyx + +class WorkerInitializerTest { + + private val workerActionExecutor = Mockito.mock(WorkerActionExecutorImpl::class.java) + private val jobDefinitionsRegistry = JobDefinitionsRegistry(setOf(BASE_WORKER_NAME), listOf(), workerActionExecutor) + + private val workerService = Mockito.mock(WorkerService::class.java) + private val triggerDescriptorService = Mockito.mock(TriggerDescriptorService::class.java) + + private val workerInitializer = WorkerInitializer( + triggerDescriptorService, + jobDefinitionsRegistry, + workerService + ) + + private val baseWorker = Worker().also { + it.workerName = BASE_WORKER_NAME + it.status = WorkerStatus.IDLE + } + + @Test + fun init_existingWorker() { + doAnswer { baseWorker }.`when`(workerService).getWithLock(BASE_WORKER_NAME) + doAnswer { it.arguments[0] }.`when`(workerService).unsetStopped(anyx()) + + workerInitializer.init() + + verify(workerService).unsetStopped(baseWorker) + verify(workerService, never()).create(anyString()) + verify(triggerDescriptorService, never()).createDefaultTriggerDescriptors(baseWorker) + } + + @Test + fun init_newWorker() { + doAnswer { null }.`when`(workerService).getWithLock(BASE_WORKER_NAME) + doAnswer { baseWorker }.`when`(workerService).create(BASE_WORKER_NAME) + + workerInitializer.init() + + verify(workerService).create(BASE_WORKER_NAME) + verify(triggerDescriptorService).createDefaultTriggerDescriptors(baseWorker) + } + + companion object { + private const val BASE_WORKER_NAME = "baseWorker" + } + +} diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt new file mode 100644 index 0000000..c74f3d0 --- /dev/null +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt @@ -0,0 +1,111 @@ +package ru.touchin.spring.workers.manager.agent.scheduled + +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers +import org.mockito.Mockito +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.verify +import org.quartz.Scheduler +import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry +import ru.touchin.spring.workers.manager.agent.services.impl.SchedulerServiceImpl +import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService + +class WorkerManagerWatcherTest { + private val testJob = Mockito.mock(BaseJob::class.java) + + private val jobDefinitionsRegistry = Mockito.mock(JobDefinitionsRegistry::class.java) + private val triggerRegistry = Mockito.mock(TriggersRegistry::class.java) + private val watcherSyncInterval = 1000L + + private val triggerDescriptorCoreService = Mockito.mock(TriggerDescriptorCoreService::class.java) + private val scheduleTriggerService = Mockito.mock(SchedulerServiceImpl::class.java) + private val quartzScheduler = Mockito.mock(Scheduler::class.java) + + private val workerManagerWatcher = WorkerManagerWatcher( + watcherSyncInterval, + jobDefinitionsRegistry, + scheduleTriggerService, + triggerRegistry, + triggerDescriptorCoreService, + quartzScheduler + ) + + private val baseWorker = Worker().also { + it.workerName = BASE_WORKER_NAME + it.status = WorkerStatus.IDLE + } + + private val currentTriggerDescriptor1 = createTriggerDescriptor("curr1", TriggerType.CRON, baseWorker) + private val actualTriggerDescriptor1 = createTriggerDescriptor("act1", TriggerType.FIXED_RATE, baseWorker) + private val actualTriggerDescriptor2 = createTriggerDescriptor("act2", TriggerType.FIXED_RATE, baseWorker) + private val currentTriggers = listOf(currentTriggerDescriptor1) + + @Before + fun setUp() { + doAnswer { currentTriggers } + .`when`(triggerRegistry).getDescriptors() + + doAnswer { mapOf(BASE_WORKER_NAME to testJob) } + .`when`(jobDefinitionsRegistry).jobs + + doAnswer { baseWorker.triggerDescriptors.toList() } + .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) + } + + @Test + fun check_onlyNewTriggersWithoutRemoving() { + baseWorker.triggerDescriptors = setOf(currentTriggerDescriptor1, actualTriggerDescriptor1) + + workerManagerWatcher.sync() + + verify(triggerRegistry).remove(emptyList()) + verify(scheduleTriggerService).unscheduleTriggers(emptyList()) + + verify(scheduleTriggerService).scheduleTriggers(listOf(actualTriggerDescriptor1)) + } + + @Test + fun check_onlyRemoveIrrelevantTriggers() { + baseWorker.triggerDescriptors = setOf() + + workerManagerWatcher.sync() + + verify(triggerRegistry).remove(currentTriggers) + verify(scheduleTriggerService).unscheduleTriggers(currentTriggers) + + verify(scheduleTriggerService).scheduleTriggers(emptyList()) + } + + @Test + fun check_saveNewAndRemoveIrrelevantTriggers() { + baseWorker.triggerDescriptors = setOf(actualTriggerDescriptor2, actualTriggerDescriptor1) + + workerManagerWatcher.sync() + + verify(triggerRegistry).remove(currentTriggers) + verify(scheduleTriggerService).unscheduleTriggers(currentTriggers) + + verify(scheduleTriggerService).scheduleTriggers(baseWorker.triggerDescriptors.toList()) + } + + private fun createTriggerDescriptor(id: String, type: TriggerType, worker: Worker): TriggerDescriptor { + return TriggerDescriptor().also { + it.id = id + it.type = type + it.expression = "expression" + it.worker = worker + } + } + + companion object { + private const val BASE_WORKER_NAME = "baseWorker" + } + +} diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt new file mode 100644 index 0000000..dd9ddec --- /dev/null +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt @@ -0,0 +1,32 @@ +package ru.touchin.spring.workers.manager.agent.utils + +import org.junit.Test + +import org.junit.Assert.* + +class GlobTest { + + @Test + fun matches() { + assertMatches("", "*****") + assertMatches("my perfect text", "my*text") + assertMatches("my perfect text", "my*?text") + assertMatches("moon", "????") + assertMatches("(abc)", "?abc?") + assertMatches("****", "????") + + assertNotMatches("", "?") + assertNotMatches("mo", "????") + assertNotMatches("moonmoon", "????") + assertNotMatches("my perfect text", "our*text") + } + + private fun assertMatches(text: String, pattern: String) { + assertTrue(Glob.matches(text, pattern)) + } + + private fun assertNotMatches(text: String, pattern: String) { + assertFalse(Glob.matches(text, pattern)) + } + +} diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/utils/MockitoUtils.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/utils/MockitoUtils.kt new file mode 100644 index 0000000..4e1e027 --- /dev/null +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/utils/MockitoUtils.kt @@ -0,0 +1,17 @@ +package ru.touchin.spring.workers.manager.utils + +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +object MockitoUtils { + fun anyx(matcher: ((T) -> Boolean)? = null): T { + @Suppress("UNCHECKED_CAST") + return if (matcher == null) { + ArgumentMatchers.any() ?: (null as T) + } else { + Mockito.argThat(matcher) ?: (null as T) + } + } + + fun anyx(sample: T): T = anyx { it == sample } +} diff --git a/spring-workers-manager-api/build.gradle.kts b/spring-workers-manager-api/build.gradle.kts new file mode 100644 index 0000000..afeec26 --- /dev/null +++ b/spring-workers-manager-api/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation(project(":spring-workers-manager-core")) + + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + implementation("org.springframework.data:spring-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-web") +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt new file mode 100644 index 0000000..c2d38c4 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt @@ -0,0 +1,41 @@ +package ru.touchin.spring.workers.manager.api.controllers + +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest +import ru.touchin.spring.workers.manager.api.services.TriggerDescriptorApiService + +@RestController +@RequestMapping("/workers/{workerName}/triggers") +class TriggerController( + private val triggerDescriptorApiService: TriggerDescriptorApiService +) { + + @PostMapping + fun createTrigger( + @PathVariable + workerName: String, + @RequestBody + body: TriggerChangeRequest + ) { + triggerDescriptorApiService.create(workerName, body) + } + + @PutMapping("/{triggerName}") + fun changeTrigger( + @PathVariable + triggerName: String, + @PathVariable + workerName: String, + @RequestBody + body: TriggerChangeRequest + ) { + triggerDescriptorApiService.update(workerName, triggerName, body) + } + +} + diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt new file mode 100644 index 0000000..69d9d6d --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt @@ -0,0 +1,65 @@ +package ru.touchin.spring.workers.manager.api.controllers + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.touchin.spring.workers.manager.api.dto.WorkerDto +import ru.touchin.spring.workers.manager.api.services.WorkerApiService +import ru.touchin.spring.workers.manager.core.services.WorkersStateService + +@RestController +@RequestMapping("/workers") +class WorkerController( + private val workerStateService: WorkersStateService, + private val workerApiService: WorkerApiService +) { + + @GetMapping + fun getWorkers(): List { + return workerApiService.getWorkers() + } + + @GetMapping("/{workerName}") + fun getWorker( + @PathVariable + workerName: String + ): WorkerDto { + return workerApiService.getWorker(workerName) + } + + @PostMapping("/{workerName}/stop") + fun stop( + @PathVariable + workerName: String + ) { + workerStateService.stop(workerName) + } + + @PostMapping("/{workerName}/start") + fun start( + @PathVariable + workerName: String + ) { + workerStateService.start(workerName) + } + + @PostMapping("/{workerName}/disable") + fun disable( + @PathVariable + workerName: String + ) { + workerStateService.disable(workerName) + } + + @PostMapping("/{workerName}/enable") + fun enable( + @PathVariable + workerName: String + ) { + workerStateService.enable(workerName) + } + +} + diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt new file mode 100644 index 0000000..20d9b7d --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt @@ -0,0 +1,21 @@ +package ru.touchin.spring.workers.manager.api.dto + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import java.time.ZonedDateTime + +data class TriggerChangeRequest( + val name: String, + val type: TriggerType, + val expression: String, + val disabled: Boolean +) + +fun TriggerChangeRequest.toModel(newTriggerDescriptor: TriggerDescriptor? = null): TriggerDescriptor { + return (newTriggerDescriptor ?: TriggerDescriptor()).also { descriptor -> + descriptor.expression = this.expression + descriptor.triggerName = this.name + descriptor.type = this.type + descriptor.disabledAt = if (this.disabled) descriptor.disabledAt ?: ZonedDateTime.now() else null + } +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt new file mode 100644 index 0000000..40b0ff3 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt @@ -0,0 +1,21 @@ +package ru.touchin.spring.workers.manager.api.dto + +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import java.time.ZonedDateTime + +data class WorkerDto( + val workerName: String, + val stoppedAt: ZonedDateTime?, + val disabledAt: ZonedDateTime?, + val status: WorkerStatus, + val parallelExecutionEnabled: Boolean +) + +fun Worker.toDto() = WorkerDto( + workerName = this.workerName, + stoppedAt = this.stoppedAt, + disabledAt = this.disabledAt, + status = this.status, + parallelExecutionEnabled = this.parallelExecutionEnabled +) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt new file mode 100644 index 0000000..86a0812 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.api.services + +import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest + +interface TriggerDescriptorApiService { + + fun update(workerName: String, triggerName: String, body: TriggerChangeRequest) + fun create(workerName: String, body: TriggerChangeRequest) + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt new file mode 100644 index 0000000..7ed5a73 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.api.services + +import ru.touchin.spring.workers.manager.api.dto.WorkerDto + +interface WorkerApiService { + + fun getWorker(workerName: String): WorkerDto + fun getWorkers(): List + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt new file mode 100644 index 0000000..24ded49 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt @@ -0,0 +1,50 @@ +package ru.touchin.spring.workers.manager.api.services.impl + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest +import ru.touchin.spring.workers.manager.api.dto.toModel +import ru.touchin.spring.workers.manager.api.services.TriggerDescriptorApiService +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.core.services.WorkerCoreService + +@Service +class TriggerDescriptorApiServiceImpl( + private val workerCoreService: WorkerCoreService, + private val triggerDescriptorCoreService: TriggerDescriptorCoreService +) : TriggerDescriptorApiService { + + @Transactional + override fun create(workerName: String, body: TriggerChangeRequest) { + val worker = workerCoreService.get(workerName) + + saveNewTrigger(body.toModel(), worker) + } + + @Transactional + override fun update(workerName: String, triggerName: String, body: TriggerChangeRequest) { + val worker = workerCoreService.get(workerName) + + val triggerDescriptor = getTriggerDescriptor(worker, triggerName) + + triggerDescriptorCoreService.delete(triggerDescriptor) + + saveNewTrigger(body.toModel(), worker) + } + + private fun getTriggerDescriptor(worker: Worker, triggerName: String): TriggerDescriptor { + return worker + .getAllTriggerDescriptors() + .firstOrNull { it.triggerName == triggerName } + ?: throw NoSuchElementException("Trigger not found by name $triggerName") + } + + private fun saveNewTrigger(triggerDescriptor: TriggerDescriptor, worker: Worker) { + triggerDescriptorCoreService.save(triggerDescriptor + .also { it.worker = worker } + ) + } + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt new file mode 100644 index 0000000..1a6f93a --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt @@ -0,0 +1,22 @@ +package ru.touchin.spring.workers.manager.api.services.impl + +import org.springframework.stereotype.Service +import ru.touchin.spring.workers.manager.api.dto.WorkerDto +import ru.touchin.spring.workers.manager.api.dto.toDto +import ru.touchin.spring.workers.manager.api.services.WorkerApiService +import ru.touchin.spring.workers.manager.core.services.WorkerCoreService + +@Service +class WorkerApiServiceImpl( + private val workerCoreService: WorkerCoreService +) : WorkerApiService { + + override fun getWorker(workerName: String): WorkerDto { + return workerCoreService.get(workerName).toDto() + } + + override fun getWorkers(): List { + return workerCoreService.getAll().map { it.toDto() } + } + +} diff --git a/spring-workers-manager-core/README.md b/spring-workers-manager-core/README.md new file mode 100644 index 0000000..c10bfb4 --- /dev/null +++ b/spring-workers-manager-core/README.md @@ -0,0 +1 @@ +# Touch Spring Workers Manager diff --git a/spring-workers-manager-core/build.gradle.kts b/spring-workers-manager-core/build.gradle.kts new file mode 100644 index 0000000..ebde761 --- /dev/null +++ b/spring-workers-manager-core/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + implementation("org.liquibase:liquibase-core") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-quartz") +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt new file mode 100644 index 0000000..61ee3e5 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt @@ -0,0 +1,37 @@ +package ru.touchin.spring.workers.manager + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.context.properties.ConfigurationPropertiesScan +import org.springframework.cache.annotation.EnableCaching +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.data.jpa.repository.config.EnableJpaRepositories + +/** + * Configuration which brings to context all the components, required to support workers manager module via annotations. + * + * You could @[org.springframework.context.annotation.Import] this configuration into your application or use [EnableWorkersManager] annotation to do it automatically + */ +@ComponentScan +@EntityScan +@EnableJpaRepositories +@EnableCaching +@ConfigurationPropertiesScan +class WorkersManagerConfiguration { + + companion object { + const val SCHEMA: String = "workers" + } + + /** + * Applies `@EnableJpaAuditing` only if it was not already applied. + * Enabling `@EnableJpaAuditing` twice will lead to application context failure. + */ + @Configuration + @ConditionalOnMissingBean(name=["jpaAuditingHandler"]) + @EnableJpaAuditing + class JpaAuditingNonConflictingDeclaration + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/config/LiquibaseRunner.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/config/LiquibaseRunner.kt new file mode 100644 index 0000000..efcc373 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/config/LiquibaseRunner.kt @@ -0,0 +1,34 @@ +package ru.touchin.spring.workers.manager.core.config + +import liquibase.Contexts +import liquibase.Liquibase +import liquibase.database.Database +import liquibase.database.DatabaseFactory +import liquibase.database.jvm.JdbcConnection +import liquibase.resource.ClassLoaderResourceAccessor +import org.springframework.stereotype.Component +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA +import javax.sql.DataSource + +@Component +class LiquibaseRunner( + private val dataSource: DataSource +) { + + fun run() = dataSource.connection.use { connection -> + val database: Database = DatabaseFactory.getInstance() + .findCorrectDatabaseImplementation(JdbcConnection(connection)) + .apply { defaultSchemaName = SCHEMA } + + val liquibase = Liquibase(MASTER_CHANGELOG_PATH, ClassLoaderResourceAccessor(), database) + + liquibase.changeLogParameters.set("schemaName", SCHEMA) + + liquibase.update(Contexts()) + } + + companion object { + private const val MASTER_CHANGELOG_PATH = "workers/db/changelog/db.changelog-master.yaml" + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt new file mode 100644 index 0000000..705c3c5 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt @@ -0,0 +1,51 @@ +package ru.touchin.spring.workers.manager.core.models + +import org.hibernate.annotations.GenericGenerator +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA +import ru.touchin.spring.workers.manager.core.models.base.BaseEntity +import ru.touchin.spring.workers.manager.core.models.enums.ExecutionStatus +import java.time.ZonedDateTime +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne +import javax.persistence.Table + +@Entity +@Table(name = "executions", schema = SCHEMA) +class Execution : BaseEntity() { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + @Column(name = "id", unique = true) + var id: String? = null + + @Column(name = "worker_name", nullable = false) + lateinit var workerName: String + + @ManyToOne + @JoinColumn(name = "trigger_id", nullable = true) + var triggerDescriptor: TriggerDescriptor? = null + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + lateinit var status: ExecutionStatus + + @Column(name = "error_message", nullable = true) + var errorMessage: String? = null + + @Column(name = "error_code", nullable = true) + var errorCode: Int? = null + + @Column(name = "started_at", nullable = true) + var startedAt: ZonedDateTime? = null + + @Column(name = "finished_at", nullable = true) + var finishedAt: ZonedDateTime? = null + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt new file mode 100644 index 0000000..46d15d3 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt @@ -0,0 +1,75 @@ +package ru.touchin.spring.workers.manager.core.models + +import org.hibernate.annotations.GenericGenerator +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA +import ru.touchin.spring.workers.manager.core.models.base.BaseEntity +import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import java.time.ZonedDateTime +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne +import javax.persistence.Table + +@Entity +@Table(name = "triggers", schema = SCHEMA) +class TriggerDescriptor : BaseEntity() { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + @Column(name = "id", unique = true) + var id: String? = null + + /** + * This field is **immutable** + */ + @Column(name = "trigger_name", nullable = false) + lateinit var triggerName: String + + /** + * This field is **immutable** + */ + @Enumerated(EnumType.STRING) + @Column(name = "type", nullable = false) + lateinit var type: TriggerType + + /** + * This field is **immutable** + */ + @ManyToOne + @JoinColumn(name = "worker_name", nullable = false) + lateinit var worker: Worker + + /** + * This field is **immutable** + */ + @Column(name = "expression", nullable = false) + lateinit var expression: String + + @Column(name = "disabled_at", nullable = true) + var disabledAt: ZonedDateTime? = null + + @Column(name = "deleted_at", nullable = true) + var deletedAt: ZonedDateTime? = null + + fun isDisabled() = disabledAt != null + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TriggerDescriptor) return false + + if (id != other.id) return false + + return true + } + + override fun hashCode(): Int { + return id?.hashCode() ?: 0 + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt new file mode 100644 index 0000000..e5c6467 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt @@ -0,0 +1,48 @@ +package ru.touchin.spring.workers.manager.core.models + +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA +import ru.touchin.spring.workers.manager.core.models.base.BaseEntity +import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import java.time.ZonedDateTime +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated +import javax.persistence.FetchType +import javax.persistence.Id +import javax.persistence.OneToMany +import javax.persistence.Table + +@Entity +@Table(name = "workers", schema = SCHEMA) +class Worker : BaseEntity() { + + @Id + @Column(name = "worker_name", unique = true) + lateinit var workerName: String + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + lateinit var status: WorkerStatus + + @Column(name = "disabled_at", nullable = true) + var disabledAt: ZonedDateTime? = null + + @Column(name = "stopped_at", nullable = true) + var stoppedAt: ZonedDateTime? = null + + @Column(name = "parallel_execution_enabled", nullable = false) + var parallelExecutionEnabled: Boolean = false + + @OneToMany(mappedBy = "worker", orphanRemoval = true, fetch = FetchType.LAZY) + lateinit var triggerDescriptors: Set + + fun isStopped() = stoppedAt != null + + fun isDisabled() = disabledAt != null + + fun getEnabledTriggerDescriptors() = triggerDescriptors.filter { !it.isDisabled() && it.deletedAt == null} + + fun getAllTriggerDescriptors() = triggerDescriptors.filter { it.deletedAt == null} + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt new file mode 100644 index 0000000..ec390ac --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt @@ -0,0 +1,24 @@ +package ru.touchin.spring.workers.manager.core.models.base + +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.io.Serializable +import java.time.LocalDateTime +import javax.persistence.Column +import javax.persistence.EntityListeners +import javax.persistence.MappedSuperclass + +@EntityListeners(AuditingEntityListener::class) +@MappedSuperclass +abstract class BaseEntity : Serializable { + + @CreatedDate + @Column(name = "created_at", nullable = false) + lateinit var createdAt: LocalDateTime + + @LastModifiedDate + @Column(name = "updated_at", nullable = true) + var updatedAt: LocalDateTime? = null + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt new file mode 100644 index 0000000..4ad937f --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt @@ -0,0 +1,8 @@ +package ru.touchin.spring.workers.manager.core.models.enums + +enum class ExecutionStatus { + + PROCESSING, + FINISHED + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt new file mode 100644 index 0000000..3dbb0b3 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt @@ -0,0 +1,47 @@ +package ru.touchin.spring.workers.manager.core.models.enums + +enum class TriggerType { + + /** + * Uses CRON expression for scheduling. + * Expression example: `0 * * * * *` + */ + CRON, + + /** + * Uses numeric expressions, which mean time period in milliseconds between + * end of previous execution and start of the next one. + * ``` + * ------------ TIMELINE ------------------- + * [ EXECUTION ]............................ + * .............< DELAY >................... + * ......................[ NEXT EXECUTION ] + * ----------------------------------------- + * ``` + */ + FIXED_DELAY, + + /** + * Uses numeric expressions, which mean time period in milliseconds between + * start of previous execution and start of the next one. + * ``` + * ------------ TIMELINE ------------------- + * [ EXECUTION ]............................ + * < DELAY >................................ + * .........[ NEXT EXECUTION ].............. + * .........< DELAY >....................... + * ..................[ 3RD EXECUTION ]..... + * ----------------------------------------- + * ``` + */ + FIXED_RATE; + + companion object { + fun find(name: String?): TriggerType? { + name ?: return null + + return values().find { it.name == name } + } + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt new file mode 100644 index 0000000..8e72a16 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt @@ -0,0 +1,8 @@ +package ru.touchin.spring.workers.manager.core.models.enums + +enum class WorkerStatus { + + IDLE, + PROCESSING + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt new file mode 100644 index 0000000..5d2f974 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt @@ -0,0 +1,6 @@ +package ru.touchin.spring.workers.manager.core.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import ru.touchin.spring.workers.manager.core.models.Execution + +interface ExecutionRepository : JpaRepository diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt new file mode 100644 index 0000000..2906710 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt @@ -0,0 +1,20 @@ +package ru.touchin.spring.workers.manager.core.repositories + +import org.springframework.data.jpa.repository.JpaRepository + +object RepositoryUtils { + + const val SKIP_LOCKED_PARAMETER = "javax.persistence.lock.timeout" + + + /** + * Const value which is used for 'SKIP LOCKED' in 'SELECT ... FOR UPDATE' query + */ + const val SKIP_LOCKED_VALUE = "-2" + +} + + +fun JpaRepository.upsert(entity: T, updater: (T) -> Unit): T { + return save(entity.also(updater)) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt new file mode 100644 index 0000000..63f94c8 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt @@ -0,0 +1,19 @@ +package ru.touchin.spring.workers.manager.core.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor + +interface TriggerDescriptorRepository : JpaRepository { + + @Query( + """ + SELECT td + FROM TriggerDescriptor td + WHERE td.worker.workerName = :workerName + AND td.deletedAt IS NULL + """ + ) + fun findAll(workerName: String): List + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt new file mode 100644 index 0000000..fef419c --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt @@ -0,0 +1,34 @@ +package ru.touchin.spring.workers.manager.core.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock +import org.springframework.data.jpa.repository.Query +import org.springframework.data.jpa.repository.QueryHints +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.repositories.RepositoryUtils.SKIP_LOCKED_PARAMETER +import ru.touchin.spring.workers.manager.core.repositories.RepositoryUtils.SKIP_LOCKED_VALUE +import javax.persistence.LockModeType +import javax.persistence.QueryHint + +interface WorkerRepository : JpaRepository { + + @Query( + """ + SELECT w + FROM Worker w + WHERE w.workerName = :workerName + """ + ) + @Lock(LockModeType.PESSIMISTIC_WRITE) + fun findWithLock(workerName: String): Worker? + + @Query( + """ + SELECT w + FROM Worker w + WHERE w.workerName = :workerName + """ + ) + fun find(workerName: String): Worker? + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt new file mode 100644 index 0000000..6195ba3 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.core.services + +import ru.touchin.spring.workers.manager.core.models.Execution +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker + +interface ExecutionService{ + + fun finishExecution(execution: Execution) + fun createExecution(worker: Worker, triggerDescriptor: TriggerDescriptor?): Execution + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt new file mode 100644 index 0000000..3923424 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt @@ -0,0 +1,11 @@ +package ru.touchin.spring.workers.manager.core.services + +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor + +interface TriggerDescriptorCoreService { + + fun save(triggerDescriptor: TriggerDescriptor) + fun delete(triggerDescriptor: TriggerDescriptor) + fun getByWorkerName(workerName: String): List + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt new file mode 100644 index 0000000..29a6dc0 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt @@ -0,0 +1,13 @@ +package ru.touchin.spring.workers.manager.core.services + +import ru.touchin.spring.workers.manager.core.models.Worker + +interface WorkerCoreService{ + + fun save(worker: Worker): Worker + fun getByNameWithLock(workerName: String): Worker? + fun get(name: String): Worker = getOrNull(name) ?: throw NoSuchElementException("Worker not found by name $name") + fun getOrNull(name: String): Worker? + fun getAll(): List + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt new file mode 100644 index 0000000..7dfe992 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.core.services + +interface WorkersStateService { + + fun stop(name: String) + fun start(name: String) + fun disable(name: String) + fun enable(name: String) + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt new file mode 100644 index 0000000..f821bdf --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt @@ -0,0 +1,34 @@ +package ru.touchin.spring.workers.manager.core.services.impl + +import org.springframework.stereotype.Service +import ru.touchin.spring.workers.manager.core.models.Execution +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.models.enums.ExecutionStatus +import ru.touchin.spring.workers.manager.core.repositories.ExecutionRepository +import ru.touchin.spring.workers.manager.core.repositories.upsert +import ru.touchin.spring.workers.manager.core.services.ExecutionService +import java.time.ZonedDateTime + +@Service +class ExecutionServiceImpl( + private val executionRepository: ExecutionRepository +) : ExecutionService { + + override fun createExecution(worker: Worker, triggerDescriptor: TriggerDescriptor?): Execution { + return executionRepository.upsert(Execution()) { + it.startedAt = ZonedDateTime.now() + it.workerName = worker.workerName + it.triggerDescriptor = triggerDescriptor + it.status = ExecutionStatus.PROCESSING + } + } + + override fun finishExecution(execution: Execution) { + executionRepository.upsert(execution) { + it.finishedAt = ZonedDateTime.now() + it.status = ExecutionStatus.FINISHED + } + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt new file mode 100644 index 0000000..cfa2939 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt @@ -0,0 +1,32 @@ +package ru.touchin.spring.workers.manager.core.services.impl + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.repositories.TriggerDescriptorRepository +import ru.touchin.spring.workers.manager.core.repositories.upsert +import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService +import java.time.ZonedDateTime + +@Service +class TriggerDescriptorCoreServiceImpl( + private val triggerDescriptorRepository: TriggerDescriptorRepository +) : TriggerDescriptorCoreService { + + @Transactional + override fun save(triggerDescriptor: TriggerDescriptor) { + triggerDescriptorRepository.save(triggerDescriptor) + } + + @Transactional + override fun delete(triggerDescriptor: TriggerDescriptor) { + triggerDescriptorRepository.upsert(triggerDescriptor) { + it.deletedAt = ZonedDateTime.now() + } + } + + override fun getByWorkerName(workerName: String): List { + return triggerDescriptorRepository.findAll(workerName) + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt new file mode 100644 index 0000000..91f54af --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt @@ -0,0 +1,25 @@ +package ru.touchin.spring.workers.manager.core.services.impl + +import org.springframework.stereotype.Service +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository +import ru.touchin.spring.workers.manager.core.services.WorkerCoreService + +@Service +class WorkerCoreServiceImpl( + private val workerRepository: WorkerRepository +) : WorkerCoreService { + + override fun save(worker: Worker): Worker { + return workerRepository.save(worker) + } + + override fun getByNameWithLock(workerName: String): Worker? { + return workerRepository.findWithLock(workerName) + } + + override fun getOrNull(name: String): Worker? = workerRepository.find(name) + + override fun getAll(): List = workerRepository.findAll() + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt new file mode 100644 index 0000000..023287c --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt @@ -0,0 +1,52 @@ +package ru.touchin.spring.workers.manager.core.services.impl + + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository +import ru.touchin.spring.workers.manager.core.repositories.upsert +import ru.touchin.spring.workers.manager.core.services.WorkersStateService +import java.time.ZonedDateTime + +@Service +class WorkerStateServiceImpl( + private val workerRepository: WorkerRepository +) : WorkersStateService { + + @Transactional + override fun stop(name: String) { + updateWorkerWithLock(name) { + it.stoppedAt = ZonedDateTime.now() + } + } + + @Transactional + override fun start(name: String) { + updateWorkerWithLock(name) { + it.stoppedAt = null + } + } + + @Transactional + override fun disable(name: String) { + updateWorkerWithLock(name) { + it.disabledAt = ZonedDateTime.now() + } + } + + @Transactional + override fun enable(name: String) { + updateWorkerWithLock(name) { + it.disabledAt = null + } + } + + private fun updateWorkerWithLock(name: String, updater: (Worker) -> Unit) { + val worker = workerRepository.findWithLock(name) + ?: throw NoSuchElementException("Worker with name $name not found") + + workerRepository.upsert(worker, updater) + } + +} diff --git a/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..3510e11 --- /dev/null +++ b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml @@ -0,0 +1,165 @@ +databaseChangeLog: + - changeSet: + id: create-table-workers + author: sonkate + preConditions: + - onFail: MARK_RAN + not: + tableExists: + tableName: workers + changes: + - createTable: + tableName: workers + columns: + - column: + name: worker_name + type: VARCHAR(250) + constraints: + nullable: false + primaryKey: true + primaryKeyName: PK_WORKERS + - column: + name: status + type: VARCHAR(250) + constraints: + nullable: false + - column: + name: parallel_execution_enabled + type: BOOLEAN + defaultValueBoolean: false + constraints: + nullable: false + - column: + name: created_at + type: TIMESTAMP WITH TIME ZONE + defaultValueDate: CURRENT_TIMESTAMP + constraints: + nullable: false + - column: + name: updated_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: stopped_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: disabled_at + type: TIMESTAMP WITH TIME ZONE + - changeSet: + id: create-table-triggers + author: sonkate + preConditions: + - onFail: MARK_RAN + not: + tableExists: + tableName: triggers + changes: + - createTable: + tableName: triggers + columns: + - column: + name: id + type: VARCHAR(250) + constraints: + nullable: false + primaryKey: true + primaryKeyName: PK_TRIGGERS + - column: + name: worker_name + type: VARCHAR(250) + constraints: + nullable: false + foreignKeyName: fk_trigger_worker + references: workers(worker_name) + - column: + name: type + type: VARCHAR(250) + constraints: + nullable: false + - column: + name: trigger_name + type: VARCHAR(250) + constraints: + nullable: false + - column: + name: expression + type: VARCHAR(250) + constraints: + nullable: false + - column: + name: created_at + type: TIMESTAMP WITH TIME ZONE + defaultValueDate: CURRENT_TIMESTAMP + constraints: + nullable: false + - column: + name: updated_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: deleted_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: disabled_at + type: TIMESTAMP WITH TIME ZONE + - changeSet: + id: create-table-executions + author: sonkate + preConditions: + - onFail: MARK_RAN + not: + tableExists: + tableName: executions + changes: + - createTable: + tableName: executions + columns: + - column: + name: id + type: VARCHAR(250) + constraints: + nullable: false + primaryKey: true + primaryKeyName: PK_EXECUTIONS + - column: + name: worker_name + type: VARCHAR(250) + constraints: + nullable: false + - column: + name: trigger_id + type: VARCHAR(250) + - column: + constraints: + nullable: false + name: status + type: VARCHAR(250) + - column: + name: error_message + type: VARCHAR(250) + - column: + name: error_code + type: int + - column: + name: created_at + type: TIMESTAMP WITH TIME ZONE + defaultValueDate: CURRENT_TIMESTAMP + constraints: + nullable: false + - column: + name: updated_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: started_at + type: TIMESTAMP WITH TIME ZONE + - column: + name: finished_at + type: TIMESTAMP WITH TIME ZONE + - changeSet: + id: create-index-for-executions + author: sonkate + changes: + - sql: + sql: | + CREATE INDEX fk_trigger_worker_idx + ON ${schemaName}.triggers (worker_name ASC) + WHERE deleted_at IS NULL; + endDelimeter: ; From 7960e5abac02d7a88074b6f1d965f2fa044fc798 Mon Sep 17 00:00:00 2001 From: Mikhail Yasnov Date: Thu, 25 Nov 2021 19:28:55 +0300 Subject: [PATCH 2/5] Update worker manager code style --- spring-workers-manager-agent/build.gradle.kts | 5 ++ .../workers/manager/agent/AgentInitializer.kt | 4 +- ...tationConfigCollectingBeanPostProcessor.kt | 2 +- ...ConfigInitialTriggerDescriptorsProvider.kt | 19 +++-- .../AnnotationConfigJobProvider.kt | 2 +- .../{DefaultTrigger.kt => InitTrigger.kt} | 2 +- .../job_factory/AnnotationConfigJobFactory.kt | 2 +- ...heduledActionAnnotationConfigJobFactory.kt | 5 +- .../AnnotationConfigTriggerFactory.kt | 6 +- .../TriggerAnnotationConfigTriggerFactory.kt | 42 ++++----- .../agent/{ => common}/base/BaseJob.kt | 2 +- .../manager/agent/{ => common}/utils/Glob.kt | 2 +- .../manager/agent/config/WorkerInitializer.kt | 14 +-- .../agent/executors/WorkerActionExecutor.kt | 13 --- .../impl/WorkerActionExecutorImpl.kt | 71 ---------------- .../manager/agent/quartz/RunnableJob.kt | 4 + .../agent/registry/JobDefinitionsRegistry.kt | 11 ++- .../manager/agent/registry/JobProvider.kt | 4 +- .../agent/registry/SimpleJobProvider.kt | 3 +- .../agent/registry/TriggersRegistry.kt | 2 +- .../agent/scheduled/WorkerManagerWatcher.kt | 10 +-- .../services/SchedulerService.kt | 4 +- .../services}/SchedulerServiceImpl.kt | 9 +- .../services/TriggerDescriptorService.kt | 12 --- .../manager/agent/services/WorkerService.kt | 12 --- .../impl/TriggerDescriptorServiceImpl.kt | 43 ---------- .../agent/services/impl/WorkerServiceImpl.kt | 36 -------- .../InitialTriggerDescriptorsProvider.kt | 14 +++ .../NoInitialTriggersDescriptorsProvider.kt | 12 +-- .../services/TriggerDescriptorService.kt | 13 +++ .../services/TriggerDescriptorServiceImpl.kt | 25 ++++++ .../InitialTriggerDescriptorsProvider.kt | 15 ---- .../worker/executors/WorkerActionExecutor.kt | 14 +++ .../executors/WorkerActionExecutorImpl.kt | 77 +++++++++++++++++ .../workers/manager/agent/TestApplication.kt | 12 +++ .../agent/{ => common}/utils/GlobTest.kt | 8 +- .../agent/config/WorkerInitializerTest.kt | 79 ++++++++++++----- .../scheduled/WorkerManagerWatcherTest.kt | 85 +++++++++++-------- .../test/resources/application-test-slow.yml | 3 + .../src/test/resources/application-test.yml | 3 + .../api/controllers/TriggerController.kt | 41 --------- .../manager/api/dto/TriggerChangeRequest.kt | 21 ----- .../workers/manager/api/dto/WorkerDto.kt | 21 ----- .../services/TriggerDescriptorApiService.kt | 10 --- .../manager/api/services/WorkerApiService.kt | 10 --- .../impl/TriggerDescriptorApiServiceImpl.kt | 50 ----------- .../api/services/impl/WorkerApiServiceImpl.kt | 22 ----- .../trigger/controllers/TriggerController.kt | 60 +++++++++++++ .../controllers/dto/TriggerChangeRequest.kt | 10 +++ .../services/TriggerDescriptorApiService.kt | 11 +++ .../TriggerDescriptorApiServiceImpl.kt | 61 +++++++++++++ .../api/trigger/services/dto/CreateTrigger.kt | 11 +++ .../api/trigger/services/dto/UpdateTrigger.kt | 12 +++ .../controllers/WorkerController.kt | 12 +-- .../worker/controllers/dto/WorkerResponse.kt | 21 +++++ .../api/worker/services/WorkerApiService.kt | 10 +++ .../worker/services/WorkerApiServiceImpl.kt | 22 +++++ spring-workers-manager-core/build.gradle.kts | 4 + .../manager/WorkersManagerConfiguration.kt | 3 +- .../converters/ExecutionConverters.kt | 15 ++++ .../manager/core/execution/dto/Execution.kt | 14 +++ .../enums/ExecutionStatus.kt | 2 +- .../exceptions/ExecutionNotFoundException.kt | 6 ++ .../models/ExecutionEntity.kt} | 20 ++--- .../repositories/ExecutionRepository.kt | 14 +++ .../services/ExecutionCoreService.kt | 12 +++ .../services/ExecutionCoreServiceImpl.kt | 49 +++++++++++ .../execution/services/dto/CreateExecution.kt | 8 ++ .../manager/core/models/base/BaseEntity.kt | 24 ------ .../manager/core/models/enums/WorkerStatus.kt | 8 -- .../core/repositories/ExecutionRepository.kt | 6 -- .../core/repositories/RepositoryUtils.kt | 20 ----- .../TriggerDescriptorRepository.kt | 19 ----- .../core/repositories/WorkerRepository.kt | 34 -------- .../manager/core/services/ExecutionService.kt | 12 --- .../services/TriggerDescriptorCoreService.kt | 11 --- .../core/services/WorkerCoreService.kt | 13 --- .../services/impl/ExecutionServiceImpl.kt | 34 -------- .../impl/TriggerDescriptorCoreServiceImpl.kt | 32 ------- .../services/impl/WorkerCoreServiceImpl.kt | 25 ------ .../converters/TriggerDescriptorConverters.kt | 16 ++++ .../core/trigger/dto/TriggerDescriptor.kt | 31 +++++++ .../{models => trigger}/enums/TriggerType.kt | 4 +- .../exceptions/TriggerNotFoundException.kt | 11 +++ .../models/TriggerDescriptorEntity.kt} | 31 ++----- .../TriggerDescriptorRepository.kt | 27 ++++++ .../services/TriggerDescriptorCoreService.kt | 14 +++ .../TriggerDescriptorCoreServiceImpl.kt | 68 +++++++++++++++ .../services/dto/CreateTriggerDescriptor.kt | 12 +++ .../worker/converters/WorkerConverters.kt | 16 ++++ .../workers/manager/core/worker/dto/Worker.kt | 23 +++++ .../manager/core/worker/enums/WorkerStatus.kt | 8 ++ .../exceptions/WorkerNotFoundException.kt | 7 ++ .../models/WorkerEntity.kt} | 19 ++--- .../worker/repositories/WorkerRepository.kt | 36 ++++++++ .../core/worker/services/WorkerCoreService.kt | 17 ++++ .../worker/services/WorkerCoreServiceImpl.kt | 56 ++++++++++++ .../services}/WorkerStateServiceImpl.kt | 13 ++- .../services/WorkersStateService.kt | 2 +- .../core/worker/services/dto/UpdateWorker.kt | 8 ++ .../db/changelog/db.changelog-master.yaml | 4 +- .../resources/application-test-slow.yml | 3 + .../kotlin/resources/application-test.yml | 3 + 103 files changed, 1104 insertions(+), 816 deletions(-) rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/{DefaultTrigger.kt => InitTrigger.kt} (91%) rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/{ => common}/base/BaseJob.kt (60%) rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/{ => common}/utils/Glob.kt (95%) delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/{ => scheduled}/services/SchedulerService.kt (55%) rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/{services/impl => scheduled/services}/SchedulerServiceImpl.kt (90%) delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/InitialTriggerDescriptorsProvider.kt rename spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/{triggers => trigger}/NoInitialTriggersDescriptorsProvider.kt (56%) create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorServiceImpl.kt delete mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutor.kt create mode 100644 spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutorImpl.kt create mode 100644 spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt rename spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/{ => common}/utils/GlobTest.kt (78%) create mode 100644 spring-workers-manager-agent/src/test/resources/application-test-slow.yml create mode 100644 spring-workers-manager-agent/src/test/resources/application-test.yml delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt delete mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/TriggerController.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/dto/TriggerChangeRequest.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiService.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiServiceImpl.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/CreateTrigger.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/UpdateTrigger.kt rename spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/{ => worker}/controllers/WorkerController.kt (78%) create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/dto/WorkerResponse.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiService.kt create mode 100644 spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/converters/ExecutionConverters.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/dto/Execution.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{models => execution}/enums/ExecutionStatus.kt (50%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/exceptions/ExecutionNotFoundException.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{models/Execution.kt => execution/models/ExecutionEntity.kt} (64%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/repositories/ExecutionRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/dto/CreateExecution.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt delete mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/converters/TriggerDescriptorConverters.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{models => trigger}/enums/TriggerType.kt (95%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/exceptions/TriggerNotFoundException.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{models/TriggerDescriptor.kt => trigger/models/TriggerDescriptorEntity.kt} (64%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/repositories/TriggerDescriptorRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreServiceImpl.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/dto/CreateTriggerDescriptor.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/converters/WorkerConverters.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/enums/WorkerStatus.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/exceptions/WorkerNotFoundException.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{models/Worker.kt => worker/models/WorkerEntity.kt} (65%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/repositories/WorkerRepository.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreService.kt create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreServiceImpl.kt rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{services/impl => worker/services}/WorkerStateServiceImpl.kt (72%) rename spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/{ => worker}/services/WorkersStateService.kt (70%) create mode 100644 spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/dto/UpdateWorker.kt create mode 100644 spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml create mode 100644 spring-workers-manager-core/src/test/kotlin/resources/application-test.yml diff --git a/spring-workers-manager-agent/build.gradle.kts b/spring-workers-manager-agent/build.gradle.kts index 1489e80..6c473be 100644 --- a/spring-workers-manager-agent/build.gradle.kts +++ b/spring-workers-manager-agent/build.gradle.kts @@ -5,6 +5,8 @@ plugins { } dependencies { + implementation(project(":common-spring")) + implementation(project(":common-spring-jpa")) implementation(project(":spring-workers-manager-core")) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") @@ -12,5 +14,8 @@ dependencies { implementation("org.springframework.data:spring-data-jpa") implementation("org.springframework.boot:spring-boot-starter-quartz") + testImplementation(project(":common-spring-test-jpa")) + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin") } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt index 7f1d2da..fc5840f 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/AgentInitializer.kt @@ -2,9 +2,9 @@ package ru.touchin.spring.workers.manager.agent import org.springframework.boot.context.event.ApplicationStartedEvent import org.springframework.context.event.EventListener -import org.springframework.core.Ordered import org.springframework.core.annotation.Order import org.springframework.stereotype.Component +import ru.touchin.common.spring.Ordered import ru.touchin.spring.workers.manager.agent.config.WorkerInitializer import ru.touchin.spring.workers.manager.agent.scheduled.WorkerManagerWatcher import ru.touchin.spring.workers.manager.core.config.LiquibaseRunner @@ -20,7 +20,7 @@ class AgentInitializer( ) { @EventListener(value = [ApplicationStartedEvent::class]) - @Order(Ordered.HIGHEST_PRECEDENCE + 500) // +500 is for "higher than any normal, but lower than any highest" + @Order(Ordered.HIGH) fun execute() { liquibase.run() workerInitializer.init() diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt index 16ca5b0..7b89ed9 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigCollectingBeanPostProcessor.kt @@ -3,7 +3,7 @@ package ru.touchin.spring.workers.manager.agent.annotation_config import org.springframework.beans.factory.config.BeanPostProcessor import org.springframework.stereotype.Component import ru.touchin.spring.workers.manager.agent.annotation_config.job_factory.AnnotationConfigJobFactory -import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import java.lang.reflect.Method /** diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt index 9d68af9..70e9b58 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigInitialTriggerDescriptorsProvider.kt @@ -4,9 +4,12 @@ import org.springframework.stereotype.Component import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap import ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factory.AnnotationConfigTriggerFactory -import ru.touchin.spring.workers.manager.agent.triggers.InitialTriggerDescriptorsProvider -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.agent.trigger.InitialTriggerDescriptorsProvider +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity @Component class AnnotationConfigInitialTriggerDescriptorsProvider( @@ -14,10 +17,10 @@ class AnnotationConfigInitialTriggerDescriptorsProvider( private val triggerFactories: List ) : InitialTriggerDescriptorsProvider { - val jobName2Triggers: MultiValueMap = LinkedMultiValueMap() + val jobName2Triggers: MultiValueMap = LinkedMultiValueMap() override fun applicableFor(worker: Worker): Boolean { - val actionMethod = triggersCollector.jobName2Method[worker.workerName] + val actionMethod = triggersCollector.jobName2Method[worker.name] ?: return false val triggers = triggerFactories.flatMap { it.create(worker, actionMethod) } @@ -26,13 +29,13 @@ class AnnotationConfigInitialTriggerDescriptorsProvider( return false } - jobName2Triggers.addAll(worker.workerName, triggers) + jobName2Triggers.addAll(worker.name, triggers) return true } - override fun createInitialTriggerDescriptors(worker: Worker): List { - return jobName2Triggers[worker.workerName].orEmpty() + override fun createInitialTriggerDescriptors(worker: Worker): List { + return jobName2Triggers[worker.name].orEmpty() } } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt index 8f7d311..18688c6 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/AnnotationConfigJobProvider.kt @@ -1,7 +1,7 @@ package ru.touchin.spring.workers.manager.agent.annotation_config import org.springframework.stereotype.Component -import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import ru.touchin.spring.workers.manager.agent.registry.JobProvider @Component diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/InitTrigger.kt similarity index 91% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/InitTrigger.kt index 059f6b4..5186f68 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/DefaultTrigger.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/InitTrigger.kt @@ -8,7 +8,7 @@ import java.lang.annotation.Inherited */ @Inherited @Target(AnnotationTarget.FUNCTION) -annotation class DefaultTrigger( +annotation class InitTrigger( val name: String = "", val type: String, val expression: String diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt index 0ef1358..873258e 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/AnnotationConfigJobFactory.kt @@ -1,6 +1,6 @@ package ru.touchin.spring.workers.manager.agent.annotation_config.job_factory -import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import java.lang.reflect.Method /** diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt index ac7585d..243b0fc 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/job_factory/ScheduledActionAnnotationConfigJobFactory.kt @@ -2,15 +2,14 @@ package ru.touchin.spring.workers.manager.agent.annotation_config.job_factory import org.springframework.stereotype.Component import ru.touchin.spring.workers.manager.agent.annotation_config.ScheduledAction -import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import java.lang.reflect.Method /** * Creates job instances for every annotated action method. */ @Component -class ScheduledActionAnnotationConfigJobFactory - : AnnotationConfigJobFactory { +class ScheduledActionAnnotationConfigJobFactory : AnnotationConfigJobFactory { override fun create(bean: Any, actionMethod: Method): List { val job = createJobForBean(bean, actionMethod) diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt index 6ff387b..89906cd 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/AnnotationConfigTriggerFactory.kt @@ -1,7 +1,7 @@ package ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factory -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker import java.lang.reflect.Method /** @@ -9,6 +9,6 @@ import java.lang.reflect.Method */ interface AnnotationConfigTriggerFactory { - fun create(worker: Worker, actionMethod: Method): List + fun create(worker: Worker, actionMethod: Method): List } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt index e503845..8d9b29a 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/annotation_config/trigger_factory/TriggerAnnotationConfigTriggerFactory.kt @@ -3,14 +3,14 @@ package ru.touchin.spring.workers.manager.agent.annotation_config.trigger_factor import org.springframework.context.EmbeddedValueResolverAware import org.springframework.stereotype.Component import org.springframework.util.StringValueResolver -import ru.touchin.spring.workers.manager.agent.annotation_config.DefaultTrigger -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import ru.touchin.spring.workers.manager.agent.annotation_config.InitTrigger +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker import java.lang.reflect.Method /** - * Creates triggers for methods annotated with [ru.touchin.spring.workers.manager.agent.annotation_config.DefaultTrigger] annotation + * Creates triggers for methods annotated with [ru.touchin.spring.workers.manager.agent.annotation_config.InitTrigger] annotation */ @Component class TriggerAnnotationConfigTriggerFactory @@ -23,23 +23,25 @@ class TriggerAnnotationConfigTriggerFactory valueResolver = resolver } - override fun create(worker: Worker, actionMethod: Method): List { - val triggerAnnotation = actionMethod.getAnnotation(DefaultTrigger::class.java) + override fun create(worker: Worker, actionMethod: Method): List { + val triggerAnnotation = actionMethod.getAnnotation(InitTrigger::class.java) ?: return emptyList() - val trigger = TriggerDescriptor().also { - it.worker = worker - - val resolvedType = valueResolver.resolveStringValue(triggerAnnotation.type) - - it.type = TriggerType.find(resolvedType) - ?: throw IllegalArgumentException("Trigger type for name $resolvedType dies not exist") - - it.expression = valueResolver.resolveStringValue(triggerAnnotation.expression) - ?: throw NullPointerException("Trigger for worker '${worker.workerName}' has null expression") - - it.triggerName = "${it.type.name}_${it.expression.replace(" ", "_")}" - } + val resolvedType = valueResolver.resolveStringValue(triggerAnnotation.type) + val triggerType = TriggerType.find(resolvedType) + ?: throw IllegalArgumentException("Trigger type for name $resolvedType dies not exist") + + val expression = valueResolver.resolveStringValue(triggerAnnotation.expression) + ?: throw NullPointerException("Trigger for worker '${worker.name}' has null expression") + + val trigger = CreateTriggerDescriptor( + name = "${triggerType.name}_${expression.replace(" ", "_")}", + type = triggerType, + workerName = worker.name, + expression = valueResolver.resolveStringValue(triggerAnnotation.expression) + ?: throw NullPointerException("Trigger for worker '${worker.name}' has null expression"), + disabledAt = null, + ) return listOf(trigger) } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/base/BaseJob.kt similarity index 60% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/base/BaseJob.kt index a010c85..ec9d17e 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/base/BaseJob.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/base/BaseJob.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.agent.base +package ru.touchin.spring.workers.manager.agent.common.base interface BaseJob { diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/Glob.kt similarity index 95% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/Glob.kt index eceea12..5444663 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/utils/Glob.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/Glob.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.agent.utils +package ru.touchin.spring.workers.manager.agent.common.utils /** * Glob is simple replacement for Regex. diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt index f0868f3..2c1cd3b 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializer.kt @@ -2,15 +2,17 @@ package ru.touchin.spring.workers.manager.agent.config import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService -import ru.touchin.spring.workers.manager.agent.services.WorkerService +import ru.touchin.spring.workers.manager.agent.trigger.services.TriggerDescriptorService import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService +import ru.touchin.spring.workers.manager.core.worker.services.WorkersStateService @Component class WorkerInitializer( private val triggerDescriptorAgentService: TriggerDescriptorService, private val jobDefinitionsRegistry: JobDefinitionsRegistry, - private val workerService: WorkerService + private val workerCoreService: WorkerCoreService, + private val workerStateService: WorkersStateService, ) { @Transactional @@ -23,13 +25,13 @@ class WorkerInitializer( } private fun getOrCreateWorkerWithTriggers(name: String) { - workerService.getWithLock(name) - ?.let(workerService::unsetStopped) + workerCoreService.getWithLock(name) + ?.let { workerStateService.start(it.name) } ?: createWorkerWithTriggers(name) } private fun createWorkerWithTriggers(name: String) { - val worker = workerService.create(name) + val worker = workerCoreService.create(name) triggerDescriptorAgentService.createDefaultTriggerDescriptors(worker) } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt deleted file mode 100644 index 7422210..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/WorkerActionExecutor.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.executors - -import org.quartz.Trigger -import ru.touchin.spring.workers.manager.agent.base.BaseJob -import ru.touchin.spring.workers.manager.core.models.Execution - -interface WorkerActionExecutor { - - fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? - fun finishWorkerProcessing(job: BaseJob) - fun executeJobAction(job: BaseJob, currentTrigger: Trigger) - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt deleted file mode 100644 index b71a3a7..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/executors/impl/WorkerActionExecutorImpl.kt +++ /dev/null @@ -1,71 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.executors.impl - -import org.quartz.Trigger -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.agent.executors.WorkerActionExecutor -import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry -import ru.touchin.spring.workers.manager.agent.base.BaseJob -import ru.touchin.spring.workers.manager.core.models.Execution -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus -import ru.touchin.spring.workers.manager.core.services.ExecutionService -import ru.touchin.spring.workers.manager.core.services.WorkerCoreService - -@Service -class WorkerActionExecutorImpl( - private val executionService: ExecutionService, - private val workerCoreService: WorkerCoreService, - private val triggersRegistry: TriggersRegistry -): WorkerActionExecutor { - - @Autowired - lateinit var workerActionExecutor: WorkerActionExecutor - - override fun executeJobAction(job: BaseJob, currentTrigger: Trigger) { - val execution = workerActionExecutor.prepareExecution(job, currentTrigger) - - execution?.let { - job.run() - - executionService.finishExecution(execution) - } - - workerActionExecutor.finishWorkerProcessing(job) - } - - @Transactional - override fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? { - val currentWorker = workerCoreService.getByNameWithLock(job.getName()) - - return currentWorker - ?.takeIf { !it.isStopped() } - ?.let { worker -> - val currentTriggerDescriptor = triggersRegistry.getDescriptorByTrigger(currentTrigger) - - currentTriggerDescriptor?.let { - setWorkerStatus(worker, WorkerStatus.PROCESSING) - - executionService.createExecution(worker, currentTriggerDescriptor) - } - } - } - - @Transactional - override fun finishWorkerProcessing(job: BaseJob) { - val currentWorker = workerCoreService.getByNameWithLock(job.getName()) - ?: return - - setWorkerStatus(currentWorker, WorkerStatus.IDLE) - } - - private fun setWorkerStatus(worker: Worker, status: WorkerStatus) { - workerCoreService.save( - worker.also { - it.status = status - } - ) - } - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt index 6d2c393..7ae3968 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/quartz/RunnableJob.kt @@ -9,6 +9,7 @@ import org.quartz.JobExecutionException typealias JobFunction = (JobExecutionContext) -> Unit class RunnableJob : Job { + override fun execute(context: JobExecutionContext) { try { @Suppress("UNCHECKED_CAST") @@ -21,6 +22,7 @@ class RunnableJob : Job { } companion object { + private const val ACTION = "ACTION" fun initJobBuilder(action: JobFunction): JobBuilder { @@ -28,5 +30,7 @@ class RunnableJob : Job { .newJob(RunnableJob::class.java) .usingJobData(JobDataMap(mapOf(ACTION to action))) } + } + } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt index 9c8eca5..3b2b5ba 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt @@ -4,10 +4,10 @@ import org.quartz.JobDetail import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import org.springframework.util.LinkedMultiValueMap -import ru.touchin.spring.workers.manager.agent.executors.WorkerActionExecutor +import ru.touchin.spring.workers.manager.agent.worker.executors.WorkerActionExecutor import ru.touchin.spring.workers.manager.agent.quartz.RunnableJob -import ru.touchin.spring.workers.manager.agent.base.BaseJob -import ru.touchin.spring.workers.manager.agent.utils.Glob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.utils.Glob @Component class JobDefinitionsRegistry( @@ -24,7 +24,10 @@ class JobDefinitionsRegistry( final val jobNames: Set init { - val allJobs = providers.flatMap { it.getJobs() } + val allJobs = providers.flatMap { + val job =it.getJobs() + job + } val name2jobsList = LinkedMultiValueMap() diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt index 887ab78..1aa5ed3 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobProvider.kt @@ -1,7 +1,9 @@ package ru.touchin.spring.workers.manager.agent.registry -import ru.touchin.spring.workers.manager.agent.base.BaseJob +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob interface JobProvider { + fun getJobs(): List + } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt index a53c3fe..2265717 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/SimpleJobProvider.kt @@ -2,8 +2,7 @@ package ru.touchin.spring.workers.manager.agent.registry import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component -import ru.touchin.spring.workers.manager.agent.base.BaseJob -import ru.touchin.spring.workers.manager.agent.registry.JobProvider +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob @Component class SimpleJobProvider : JobProvider { diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt index f4a96c5..6dc3bbf 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/TriggersRegistry.kt @@ -2,7 +2,7 @@ package ru.touchin.spring.workers.manager.agent.registry import org.quartz.Trigger import org.springframework.stereotype.Component -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor import java.util.concurrent.ConcurrentHashMap @Component diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt index 63cc5a8..dc2a5df 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt @@ -7,9 +7,9 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import ru.touchin.spring.workers.manager.agent.quartz.RunnableJob import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry -import ru.touchin.spring.workers.manager.agent.services.SchedulerService +import ru.touchin.spring.workers.manager.agent.scheduled.services.SchedulerService import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry -import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.core.trigger.services.TriggerDescriptorCoreService /** * Class is proceeding regular synchronisation trigger descriptors from db and quartz scheduled triggers @@ -49,13 +49,13 @@ class WorkerManagerWatcher( val actualTriggerDescriptors = jobDefinitionsRegistry.jobs .flatMap { (jobName, _) -> triggerDescriptorCoreService.getByWorkerName(jobName) } - .filter { !it.isDisabled() } + .filter { it.disabledAt == null } - val deletedTriggerDescriptors = currentTriggerDescriptors - actualTriggerDescriptors + val deletedTriggerDescriptors = currentTriggerDescriptors - actualTriggerDescriptors.toSet() scheduleTriggerService.unscheduleTriggers(deletedTriggerDescriptors) triggersRegistry.remove(deletedTriggerDescriptors) - val newTriggerDescriptors = actualTriggerDescriptors - currentTriggerDescriptors + val newTriggerDescriptors = actualTriggerDescriptors - currentTriggerDescriptors.toSet() scheduleTriggerService.scheduleTriggers(newTriggerDescriptors) } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerService.kt similarity index 55% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerService.kt index bc45d32..61b7294 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/SchedulerService.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerService.kt @@ -1,6 +1,6 @@ -package ru.touchin.spring.workers.manager.agent.services +package ru.touchin.spring.workers.manager.agent.scheduled.services -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor interface SchedulerService { diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerServiceImpl.kt similarity index 90% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerServiceImpl.kt index 651a226..400d281 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/SchedulerServiceImpl.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/services/SchedulerServiceImpl.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.agent.services.impl +package ru.touchin.spring.workers.manager.agent.scheduled.services import org.quartz.CronScheduleBuilder import org.quartz.JobDetail @@ -10,9 +10,8 @@ import org.quartz.TriggerBuilder import org.springframework.stereotype.Service import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry -import ru.touchin.spring.workers.manager.agent.services.SchedulerService -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType @Service class SchedulerServiceImpl( @@ -40,7 +39,7 @@ class SchedulerServiceImpl( } private fun scheduleTrigger(descriptor: TriggerDescriptor) { - val jobDetail = jobDefinitionsRegistry.getJobDetail(descriptor.worker.workerName) + val jobDetail = jobDefinitionsRegistry.getJobDetail(descriptor.workerName) if (jobDetail != null) { val trigger = createTrigger(jobDetail, descriptor) diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt deleted file mode 100644 index f1a4d94..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/TriggerDescriptorService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.services - -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import java.time.ZonedDateTime - -interface TriggerDescriptorService { - - fun createDefaultTriggerDescriptors(worker: Worker): List - fun setTriggerDescriptorDisable(triggerDescriptor: TriggerDescriptor, disabledAt: ZonedDateTime?) - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt deleted file mode 100644 index e2aab84..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/WorkerService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.services - -import ru.touchin.spring.workers.manager.core.models.Worker - -interface WorkerService { - - fun get(name: String): Worker? - fun getWithLock(name: String): Worker? - fun create(name: String): Worker - fun unsetStopped(worker: Worker): Worker - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt deleted file mode 100644 index 6b8a06b..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/TriggerDescriptorServiceImpl.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.services.impl - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService -import ru.touchin.spring.workers.manager.agent.triggers.InitialTriggerDescriptorsProvider -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.repositories.TriggerDescriptorRepository -import ru.touchin.spring.workers.manager.core.repositories.upsert -import java.time.ZonedDateTime - -@Service -class TriggerDescriptorServiceImpl( - private val triggerDescriptorRepository: TriggerDescriptorRepository, - private val initialTriggerDescriptorProviders: List -) : TriggerDescriptorService { - - @Transactional - override fun createDefaultTriggerDescriptors(worker: Worker): List { - return triggerDescriptorRepository.saveAll( - initialTriggerDescriptorProviders - .filter { it.applicableFor(worker) } - .flatMap { it.createInitialTriggerDescriptors(worker) } - ) - } - - @Transactional - override fun setTriggerDescriptorDisable( - triggerDescriptor: TriggerDescriptor, - disabledAt: ZonedDateTime? - ) { - triggerDescriptorRepository.upsert(triggerDescriptor) { - it.disabledAt = disabledAt - } - } - - private fun defaultTriggerName(descriptor: TriggerDescriptor): String{ - return "${descriptor.type}_${descriptor.expression.replace(" ", "_")}" - - } - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt deleted file mode 100644 index fac2ac7..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/services/impl/WorkerServiceImpl.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.services.impl - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.agent.services.WorkerService -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus -import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository -import ru.touchin.spring.workers.manager.core.repositories.upsert - -@Service -class WorkerServiceImpl( - private val workerRepository: WorkerRepository -) : WorkerService { - - @Transactional - override fun getWithLock(name: String): Worker? = workerRepository.findWithLock(name) - - override fun get(name: String): Worker? = workerRepository.find(name) - - override fun create(name: String): Worker { - return workerRepository.upsert(Worker()) { worker -> - worker.workerName = name - worker.status = WorkerStatus.IDLE - } - } - - override fun unsetStopped(worker: Worker): Worker { - return workerRepository.upsert(worker) { startedWorker -> - startedWorker.stoppedAt = null - } - } - - - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/InitialTriggerDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/InitialTriggerDescriptorsProvider.kt new file mode 100644 index 0000000..6823f42 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/InitialTriggerDescriptorsProvider.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.agent.trigger + +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker + +/** + * When the agent is starting first time, then initial (default) triggers should be created for new workers. + */ +interface InitialTriggerDescriptorsProvider { + + fun applicableFor(worker: Worker): Boolean + fun createInitialTriggerDescriptors(worker: Worker): List + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/NoInitialTriggersDescriptorsProvider.kt similarity index 56% rename from spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt rename to spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/NoInitialTriggersDescriptorsProvider.kt index befb24f..5159714 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/NoInitialTriggersDescriptorsProvider.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/NoInitialTriggersDescriptorsProvider.kt @@ -1,20 +1,20 @@ -package ru.touchin.spring.workers.manager.agent.triggers +package ru.touchin.spring.workers.manager.agent.trigger -import org.springframework.core.Ordered import org.springframework.core.annotation.Order import org.springframework.stereotype.Component -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker +import ru.touchin.common.spring.Ordered +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker /** * When no triggers could be created by any provider, then no triggers created. */ @Component -@Order(Ordered.LOWEST_PRECEDENCE) +@Order(Ordered.LOWER) class NoInitialTriggersDescriptorsProvider : InitialTriggerDescriptorsProvider { override fun applicableFor(worker: Worker): Boolean = true - override fun createInitialTriggerDescriptors(worker: Worker): List = emptyList() + override fun createInitialTriggerDescriptors(worker: Worker): List = emptyList() } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt new file mode 100644 index 0000000..1d2bb58 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt @@ -0,0 +1,13 @@ +package ru.touchin.spring.workers.manager.agent.trigger.services + +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity +import java.time.ZonedDateTime + +interface TriggerDescriptorService { + + fun createDefaultTriggerDescriptors(worker: Worker): List + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorServiceImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorServiceImpl.kt new file mode 100644 index 0000000..ba022d8 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorServiceImpl.kt @@ -0,0 +1,25 @@ +package ru.touchin.spring.workers.manager.agent.trigger.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.trigger.InitialTriggerDescriptorsProvider +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.core.worker.dto.Worker + +@Service +class TriggerDescriptorServiceImpl( + private val triggerDescriptorCoreService: TriggerDescriptorCoreService, + private val initialTriggerDescriptorProviders: List, +) : TriggerDescriptorService { + + @Transactional + override fun createDefaultTriggerDescriptors(worker: Worker): List { + return triggerDescriptorCoreService.create( + initialTriggerDescriptorProviders + .filter { it.applicableFor(worker) } + .flatMap { it.createInitialTriggerDescriptors(worker) } + ) + } + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt deleted file mode 100644 index e0fe37c..0000000 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/triggers/InitialTriggerDescriptorsProvider.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ru.touchin.spring.workers.manager.agent.triggers - -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker - -/** - * When the agent is starting first time, then initial (default) triggers should be created for new workers. - */ -interface InitialTriggerDescriptorsProvider { - - fun applicableFor(worker: Worker): Boolean - - fun createInitialTriggerDescriptors(worker: Worker): List - -} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutor.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutor.kt new file mode 100644 index 0000000..749c78e --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutor.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.agent.worker.executors + +import org.quartz.Trigger +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob +import ru.touchin.spring.workers.manager.core.execution.dto.Execution +import ru.touchin.spring.workers.manager.core.execution.models.ExecutionEntity + +interface WorkerActionExecutor { + + fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? + fun finishWorkerProcessing(job: BaseJob) + fun executeJobAction(job: BaseJob, currentTrigger: Trigger) + +} diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutorImpl.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutorImpl.kt new file mode 100644 index 0000000..275cd31 --- /dev/null +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/worker/executors/WorkerActionExecutorImpl.kt @@ -0,0 +1,77 @@ +package ru.touchin.spring.workers.manager.agent.worker.executors + +import org.quartz.Trigger +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob +import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry +import ru.touchin.spring.workers.manager.core.execution.dto.Execution +import ru.touchin.spring.workers.manager.core.execution.services.ExecutionCoreService +import ru.touchin.spring.workers.manager.core.execution.services.dto.CreateExecution +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService +import ru.touchin.spring.workers.manager.core.worker.services.dto.UpdateWorker + +@Service +class WorkerActionExecutorImpl( + private val executionCoreService: ExecutionCoreService, + private val workerCoreService: WorkerCoreService, + private val triggersRegistry: TriggersRegistry, +) : WorkerActionExecutor { + + @Autowired + lateinit var workerActionExecutor: WorkerActionExecutor + + override fun executeJobAction(job: BaseJob, currentTrigger: Trigger) { + val execution = workerActionExecutor.prepareExecution(job, currentTrigger) + + execution?.let { + job.run() + + executionCoreService.setFinished(execution.id) + } + + workerActionExecutor.finishWorkerProcessing(job) + } + + @Transactional + override fun prepareExecution(job: BaseJob, currentTrigger: Trigger): Execution? { + val currentWorker = workerCoreService.getWithLock(job.getName()) + + return currentWorker + ?.takeIf { !it.isStopped() } + ?.let { worker -> + val currentTriggerDescriptor = triggersRegistry.getDescriptorByTrigger(currentTrigger) + + currentTriggerDescriptor?.let { + setWorkerStatus(worker.name, WorkerStatus.PROCESSING) + + executionCoreService.create( + CreateExecution( + workerName = worker.name, + triggerId = currentTriggerDescriptor.id, + ) + ) + } + } + } + + @Transactional + override fun finishWorkerProcessing(job: BaseJob) { + val currentWorker = workerCoreService.getWithLock(job.getName()) + ?: return + + setWorkerStatus(currentWorker.name, WorkerStatus.IDLE) + } + + private fun setWorkerStatus(name: String, status: WorkerStatus) { + workerCoreService.update( + UpdateWorker( + name = name, + status = status, + ) + ) + } + +} diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt new file mode 100644 index 0000000..acc6024 --- /dev/null +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.agent + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Import +import ru.touchin.spring.workers.manager.WorkersManagerConfiguration + +@SpringBootApplication +@TestConfiguration +@Import(WorkersManagerConfiguration::class) +class TestApplication diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt similarity index 78% rename from spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt rename to spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt index dd9ddec..615952a 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/utils/GlobTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt @@ -1,8 +1,8 @@ -package ru.touchin.spring.workers.manager.agent.utils +package ru.touchin.spring.workers.manager.agent.common.utils -import org.junit.Test - -import org.junit.Assert.* +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test class GlobTest { diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt index b938860..9677ebd 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt @@ -1,62 +1,103 @@ package ru.touchin.spring.workers.manager.agent.config -import org.junit.Test +import com.nhaarman.mockitokotlin2.doNothing +import com.nhaarman.mockitokotlin2.doReturn +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.Mockito.doAnswer import org.mockito.Mockito.never import org.mockito.Mockito.verify -import ru.touchin.spring.workers.manager.agent.executors.impl.WorkerActionExecutorImpl +import org.springframework.test.util.ReflectionTestUtils +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry -import ru.touchin.spring.workers.manager.agent.services.TriggerDescriptorService -import ru.touchin.spring.workers.manager.agent.services.WorkerService -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.agent.registry.SimpleJobProvider +import ru.touchin.spring.workers.manager.agent.trigger.services.TriggerDescriptorService +import ru.touchin.spring.workers.manager.agent.worker.executors.WorkerActionExecutorImpl +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService +import ru.touchin.spring.workers.manager.core.worker.services.WorkersStateService import ru.touchin.spring.workers.manager.utils.MockitoUtils.anyx class WorkerInitializerTest { + private val simpleJobProvider = Mockito.mock(SimpleJobProvider::class.java) + + private val baseJob = Mockito.mock(BaseJob::class.java) + + private val workerActionExecutor = Mockito.mock(WorkerActionExecutorImpl::class.java) - private val jobDefinitionsRegistry = JobDefinitionsRegistry(setOf(BASE_WORKER_NAME), listOf(), workerActionExecutor) - private val workerService = Mockito.mock(WorkerService::class.java) + private val jobDefinitionsRegistry = JobDefinitionsRegistry(setOf(BASE_WORKER_NAME), listOf(simpleJobProvider), workerActionExecutor) + private val workerCoreService = Mockito.mock(WorkerCoreService::class.java) + + private val workersStateService = Mockito.mock(WorkersStateService::class.java) + private val triggerDescriptorService = Mockito.mock(TriggerDescriptorService::class.java) private val workerInitializer = WorkerInitializer( triggerDescriptorService, jobDefinitionsRegistry, - workerService + workerCoreService, + workersStateService, ) - private val baseWorker = Worker().also { - it.workerName = BASE_WORKER_NAME - it.status = WorkerStatus.IDLE + private val baseWorker = Worker( + name = BASE_WORKER_NAME, + status = WorkerStatus.IDLE, + disabledAt = null, + stoppedAt = null, + parallelExecutionEnabled = false, + ) + +// private val baseJob = object : BaseJob { +// override fun run(){} +// +// override fun getName() = BASE_WORKER_NAME +// } + + @BeforeEach + fun setUp() { + doAnswer { BASE_WORKER_NAME }.`when`(baseJob).getName() + `when`(simpleJobProvider.jobBeans).doReturn(listOf(baseJob)) + doAnswer { listOf(baseJob) }.`when`(simpleJobProvider).getJobs() + +// doAnswer { mapOf(BASE_WORKER_NAME to baseWorker) } +// .`when`(jobDefinitionsRegistry).jobs } @Test fun init_existingWorker() { - doAnswer { baseWorker }.`when`(workerService).getWithLock(BASE_WORKER_NAME) - doAnswer { it.arguments[0] }.`when`(workerService).unsetStopped(anyx()) + doAnswer { baseWorker }.`when`(workerCoreService).getWithLock(BASE_WORKER_NAME) + doNothing().`when`(workersStateService).start(anyx()) +// val jobs = + doAnswer { listOf(baseJob) }.`when`(simpleJobProvider).getJobs() + workerInitializer.init() - verify(workerService).unsetStopped(baseWorker) - verify(workerService, never()).create(anyString()) + verify(workersStateService).start(baseWorker.name) + verify(workerCoreService, never()).create(anyString()) verify(triggerDescriptorService, never()).createDefaultTriggerDescriptors(baseWorker) } @Test fun init_newWorker() { - doAnswer { null }.`when`(workerService).getWithLock(BASE_WORKER_NAME) - doAnswer { baseWorker }.`when`(workerService).create(BASE_WORKER_NAME) + doAnswer { null }.`when`(workerCoreService).getWithLock(BASE_WORKER_NAME) + doAnswer { baseWorker }.`when`(workerCoreService).create(BASE_WORKER_NAME) workerInitializer.init() - verify(workerService).create(BASE_WORKER_NAME) + verify(workerCoreService).create(BASE_WORKER_NAME) verify(triggerDescriptorService).createDefaultTriggerDescriptors(baseWorker) } companion object { + private const val BASE_WORKER_NAME = "baseWorker" } diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt index c74f3d0..6667176 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt @@ -1,23 +1,30 @@ package ru.touchin.spring.workers.manager.agent.scheduled -import org.junit.Before -import org.junit.Test +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.doReturn +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers import org.mockito.Mockito +import org.mockito.Mockito.anyString import org.mockito.Mockito.doAnswer import org.mockito.Mockito.verify import org.quartz.Scheduler +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry -import ru.touchin.spring.workers.manager.agent.services.impl.SchedulerServiceImpl -import ru.touchin.spring.workers.manager.agent.base.BaseJob -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.TriggerType -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus -import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.agent.scheduled.services.SchedulerServiceImpl +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType +import ru.touchin.spring.workers.manager.core.trigger.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import java.util.* class WorkerManagerWatcherTest { + private val testJob = Mockito.mock(BaseJob::class.java) private val jobDefinitionsRegistry = Mockito.mock(JobDefinitionsRegistry::class.java) @@ -37,31 +44,35 @@ class WorkerManagerWatcherTest { quartzScheduler ) - private val baseWorker = Worker().also { - it.workerName = BASE_WORKER_NAME - it.status = WorkerStatus.IDLE - } + private val baseWorker = Worker( + name = BASE_WORKER_NAME, + status = WorkerStatus.IDLE, + disabledAt = null, + stoppedAt = null, + parallelExecutionEnabled = false, + ) - private val currentTriggerDescriptor1 = createTriggerDescriptor("curr1", TriggerType.CRON, baseWorker) - private val actualTriggerDescriptor1 = createTriggerDescriptor("act1", TriggerType.FIXED_RATE, baseWorker) - private val actualTriggerDescriptor2 = createTriggerDescriptor("act2", TriggerType.FIXED_RATE, baseWorker) + private val triggerDescriptorId1 = UUID.fromString("514fb14b-d7ea-4ace-b200-4e06e80c37b7") + private val triggerDescriptorId2 = UUID.fromString("94d2ef98-6fc9-4d41-a66a-35b73a2448e0") + private val triggerDescriptorId3 = UUID.fromString("2a7719ec-6a65-481e-947b-08537ada2337") + private val currentTriggerDescriptor1 = createTriggerDescriptor(triggerDescriptorId1, TriggerType.CRON, baseWorker) + private val actualTriggerDescriptor1 = createTriggerDescriptor(triggerDescriptorId2, TriggerType.FIXED_RATE, baseWorker) + private val actualTriggerDescriptor2 = createTriggerDescriptor(triggerDescriptorId3, TriggerType.FIXED_RATE, baseWorker) private val currentTriggers = listOf(currentTriggerDescriptor1) - @Before + @BeforeEach fun setUp() { - doAnswer { currentTriggers } + doReturn(currentTriggers) .`when`(triggerRegistry).getDescriptors() - doAnswer { mapOf(BASE_WORKER_NAME to testJob) } + doReturn(mapOf(BASE_WORKER_NAME to testJob)) .`when`(jobDefinitionsRegistry).jobs - - doAnswer { baseWorker.triggerDescriptors.toList() } - .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) } - + @Test fun check_onlyNewTriggersWithoutRemoving() { - baseWorker.triggerDescriptors = setOf(currentTriggerDescriptor1, actualTriggerDescriptor1) + doAnswer {setOf(currentTriggerDescriptor1, actualTriggerDescriptor1)} + .`when`(triggerDescriptorCoreService).getByWorkerName(anyString()) workerManagerWatcher.sync() @@ -73,7 +84,8 @@ class WorkerManagerWatcherTest { @Test fun check_onlyRemoveIrrelevantTriggers() { - baseWorker.triggerDescriptors = setOf() + doAnswer { emptySet() } + .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) workerManagerWatcher.sync() @@ -85,26 +97,31 @@ class WorkerManagerWatcherTest { @Test fun check_saveNewAndRemoveIrrelevantTriggers() { - baseWorker.triggerDescriptors = setOf(actualTriggerDescriptor2, actualTriggerDescriptor1) + doAnswer { setOf(actualTriggerDescriptor2, actualTriggerDescriptor1) } + .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) workerManagerWatcher.sync() verify(triggerRegistry).remove(currentTriggers) verify(scheduleTriggerService).unscheduleTriggers(currentTriggers) - verify(scheduleTriggerService).scheduleTriggers(baseWorker.triggerDescriptors.toList()) + verify(scheduleTriggerService).scheduleTriggers(setOf(actualTriggerDescriptor2, actualTriggerDescriptor1).toList()) } - private fun createTriggerDescriptor(id: String, type: TriggerType, worker: Worker): TriggerDescriptor { - return TriggerDescriptor().also { - it.id = id - it.type = type - it.expression = "expression" - it.worker = worker - } + private fun createTriggerDescriptor(id: UUID, type: TriggerType, worker: Worker): TriggerDescriptor { + return TriggerDescriptor( + id = id, + name = id.toString(), + type = type, + expression = "expression", + workerName = worker.name, + disabledAt = null, + deletedAt = null, + ) } companion object { + private const val BASE_WORKER_NAME = "baseWorker" } diff --git a/spring-workers-manager-agent/src/test/resources/application-test-slow.yml b/spring-workers-manager-agent/src/test/resources/application-test-slow.yml new file mode 100644 index 0000000..40fc4f3 --- /dev/null +++ b/spring-workers-manager-agent/src/test/resources/application-test-slow.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test-slow.yml" diff --git a/spring-workers-manager-agent/src/test/resources/application-test.yml b/spring-workers-manager-agent/src/test/resources/application-test.yml new file mode 100644 index 0000000..956da22 --- /dev/null +++ b/spring-workers-manager-agent/src/test/resources/application-test.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test.yml" diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt deleted file mode 100644 index c2d38c4..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/TriggerController.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.touchin.spring.workers.manager.api.controllers - -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.PutMapping -import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest -import ru.touchin.spring.workers.manager.api.services.TriggerDescriptorApiService - -@RestController -@RequestMapping("/workers/{workerName}/triggers") -class TriggerController( - private val triggerDescriptorApiService: TriggerDescriptorApiService -) { - - @PostMapping - fun createTrigger( - @PathVariable - workerName: String, - @RequestBody - body: TriggerChangeRequest - ) { - triggerDescriptorApiService.create(workerName, body) - } - - @PutMapping("/{triggerName}") - fun changeTrigger( - @PathVariable - triggerName: String, - @PathVariable - workerName: String, - @RequestBody - body: TriggerChangeRequest - ) { - triggerDescriptorApiService.update(workerName, triggerName, body) - } - -} - diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt deleted file mode 100644 index 20d9b7d..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/TriggerChangeRequest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ru.touchin.spring.workers.manager.api.dto - -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.enums.TriggerType -import java.time.ZonedDateTime - -data class TriggerChangeRequest( - val name: String, - val type: TriggerType, - val expression: String, - val disabled: Boolean -) - -fun TriggerChangeRequest.toModel(newTriggerDescriptor: TriggerDescriptor? = null): TriggerDescriptor { - return (newTriggerDescriptor ?: TriggerDescriptor()).also { descriptor -> - descriptor.expression = this.expression - descriptor.triggerName = this.name - descriptor.type = this.type - descriptor.disabledAt = if (this.disabled) descriptor.disabledAt ?: ZonedDateTime.now() else null - } -} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt deleted file mode 100644 index 40b0ff3..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/dto/WorkerDto.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ru.touchin.spring.workers.manager.api.dto - -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus -import java.time.ZonedDateTime - -data class WorkerDto( - val workerName: String, - val stoppedAt: ZonedDateTime?, - val disabledAt: ZonedDateTime?, - val status: WorkerStatus, - val parallelExecutionEnabled: Boolean -) - -fun Worker.toDto() = WorkerDto( - workerName = this.workerName, - stoppedAt = this.stoppedAt, - disabledAt = this.disabledAt, - status = this.status, - parallelExecutionEnabled = this.parallelExecutionEnabled -) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt deleted file mode 100644 index 86a0812..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/TriggerDescriptorApiService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ru.touchin.spring.workers.manager.api.services - -import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest - -interface TriggerDescriptorApiService { - - fun update(workerName: String, triggerName: String, body: TriggerChangeRequest) - fun create(workerName: String, body: TriggerChangeRequest) - -} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt deleted file mode 100644 index 7ed5a73..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/WorkerApiService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ru.touchin.spring.workers.manager.api.services - -import ru.touchin.spring.workers.manager.api.dto.WorkerDto - -interface WorkerApiService { - - fun getWorker(workerName: String): WorkerDto - fun getWorkers(): List - -} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt deleted file mode 100644 index 24ded49..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/TriggerDescriptorApiServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ru.touchin.spring.workers.manager.api.services.impl - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.api.dto.TriggerChangeRequest -import ru.touchin.spring.workers.manager.api.dto.toModel -import ru.touchin.spring.workers.manager.api.services.TriggerDescriptorApiService -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService -import ru.touchin.spring.workers.manager.core.services.WorkerCoreService - -@Service -class TriggerDescriptorApiServiceImpl( - private val workerCoreService: WorkerCoreService, - private val triggerDescriptorCoreService: TriggerDescriptorCoreService -) : TriggerDescriptorApiService { - - @Transactional - override fun create(workerName: String, body: TriggerChangeRequest) { - val worker = workerCoreService.get(workerName) - - saveNewTrigger(body.toModel(), worker) - } - - @Transactional - override fun update(workerName: String, triggerName: String, body: TriggerChangeRequest) { - val worker = workerCoreService.get(workerName) - - val triggerDescriptor = getTriggerDescriptor(worker, triggerName) - - triggerDescriptorCoreService.delete(triggerDescriptor) - - saveNewTrigger(body.toModel(), worker) - } - - private fun getTriggerDescriptor(worker: Worker, triggerName: String): TriggerDescriptor { - return worker - .getAllTriggerDescriptors() - .firstOrNull { it.triggerName == triggerName } - ?: throw NoSuchElementException("Trigger not found by name $triggerName") - } - - private fun saveNewTrigger(triggerDescriptor: TriggerDescriptor, worker: Worker) { - triggerDescriptorCoreService.save(triggerDescriptor - .also { it.worker = worker } - ) - } - -} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt deleted file mode 100644 index 1a6f93a..0000000 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/services/impl/WorkerApiServiceImpl.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ru.touchin.spring.workers.manager.api.services.impl - -import org.springframework.stereotype.Service -import ru.touchin.spring.workers.manager.api.dto.WorkerDto -import ru.touchin.spring.workers.manager.api.dto.toDto -import ru.touchin.spring.workers.manager.api.services.WorkerApiService -import ru.touchin.spring.workers.manager.core.services.WorkerCoreService - -@Service -class WorkerApiServiceImpl( - private val workerCoreService: WorkerCoreService -) : WorkerApiService { - - override fun getWorker(workerName: String): WorkerDto { - return workerCoreService.get(workerName).toDto() - } - - override fun getWorkers(): List { - return workerCoreService.getAll().map { it.toDto() } - } - -} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/TriggerController.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/TriggerController.kt new file mode 100644 index 0000000..0341fb8 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/TriggerController.kt @@ -0,0 +1,60 @@ +package ru.touchin.spring.workers.manager.api.trigger.controllers + +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.touchin.spring.workers.manager.api.trigger.controllers.dto.TriggerChangeRequest +import ru.touchin.spring.workers.manager.api.trigger.services.TriggerDescriptorApiService +import ru.touchin.spring.workers.manager.api.trigger.services.dto.CreateTrigger +import ru.touchin.spring.workers.manager.api.trigger.services.dto.UpdateTrigger + +@RestController +@RequestMapping("/workers/{workerName}/triggers") +class TriggerController( + private val triggerDescriptorApiService: TriggerDescriptorApiService +) { + + @PostMapping + fun createTrigger( + @PathVariable + workerName: String, + @RequestBody + body: TriggerChangeRequest + ) { + triggerDescriptorApiService.create( + CreateTrigger( + workerName = workerName, + triggerName = body.name, + type = body.type, + expression = body.expression, + disabled = body.disabled, + ) + ) + } + + @PutMapping("/{triggerName}") + fun changeTrigger( + @PathVariable + triggerName: String, + @PathVariable + workerName: String, + @RequestBody + body: TriggerChangeRequest + ) { + triggerDescriptorApiService.update( + UpdateTrigger( + workerName = workerName, + oldTriggerName = triggerName, + newTriggerName = body.name, + type = body.type, + expression = body.expression, + disabled = body.disabled, + ) + ) + } + +} + diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/dto/TriggerChangeRequest.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/dto/TriggerChangeRequest.kt new file mode 100644 index 0000000..59f3742 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/controllers/dto/TriggerChangeRequest.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.api.trigger.controllers.dto + +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType + +data class TriggerChangeRequest( + val name: String, + val type: TriggerType, + val expression: String, + val disabled: Boolean, +) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiService.kt new file mode 100644 index 0000000..81b68a3 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiService.kt @@ -0,0 +1,11 @@ +package ru.touchin.spring.workers.manager.api.trigger.services + +import ru.touchin.spring.workers.manager.api.trigger.services.dto.CreateTrigger +import ru.touchin.spring.workers.manager.api.trigger.services.dto.UpdateTrigger + +interface TriggerDescriptorApiService { + + fun create(create: CreateTrigger) + fun update(update: UpdateTrigger) + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiServiceImpl.kt new file mode 100644 index 0000000..ad38d3f --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/TriggerDescriptorApiServiceImpl.kt @@ -0,0 +1,61 @@ +package ru.touchin.spring.workers.manager.api.trigger.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.api.trigger.services.dto.CreateTrigger +import ru.touchin.spring.workers.manager.api.trigger.services.dto.UpdateTrigger +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.exceptions.TriggerNotFoundException +import ru.touchin.spring.workers.manager.core.trigger.services.TriggerDescriptorCoreService +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService +import java.time.ZonedDateTime + +@Service +class TriggerDescriptorApiServiceImpl( + private val workerCoreService: WorkerCoreService, + private val triggerDescriptorCoreService: TriggerDescriptorCoreService, +) : TriggerDescriptorApiService { + + @Transactional + override fun create(create: CreateTrigger) { + val worker = workerCoreService.get(create.workerName) + + triggerDescriptorCoreService.create( + CreateTriggerDescriptor( + expression = create.expression, + name = create.triggerName, + type = create.type, + disabledAt = if (create.disabled) ZonedDateTime.now() else null, + workerName = worker.name, + ) + ) + } + + @Transactional + override fun update(update: UpdateTrigger) { + val worker = workerCoreService.get(update.workerName) + + val triggerDescriptor = getTriggerDescriptor(worker, update.oldTriggerName) + + triggerDescriptorCoreService.setDeleted(triggerDescriptor.id) + + triggerDescriptorCoreService.create( + CreateTriggerDescriptor( + expression = update.expression, + name = update.newTriggerName, + type = update.type, + disabledAt = if (update.disabled) ZonedDateTime.now() else null, + workerName = worker.name, + ) + ) + } + + private fun getTriggerDescriptor(worker: Worker, triggerName: String): TriggerDescriptor { + return triggerDescriptorCoreService.getByWorkerName(worker.name) + .firstOrNull { it.name == triggerName } + ?: throw TriggerNotFoundException(triggerName) + } + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/CreateTrigger.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/CreateTrigger.kt new file mode 100644 index 0000000..d58ef17 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/CreateTrigger.kt @@ -0,0 +1,11 @@ +package ru.touchin.spring.workers.manager.api.trigger.services.dto + +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType + +data class CreateTrigger( + val workerName: String, + val triggerName: String, + val type: TriggerType, + val expression: String, + val disabled: Boolean, +) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/UpdateTrigger.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/UpdateTrigger.kt new file mode 100644 index 0000000..6d79552 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/trigger/services/dto/UpdateTrigger.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.api.trigger.services.dto + +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType + +data class UpdateTrigger( + val workerName: String, + val oldTriggerName: String, + val newTriggerName: String, + val type: TriggerType, + val expression: String, + val disabled: Boolean, +) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/WorkerController.kt similarity index 78% rename from spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt rename to spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/WorkerController.kt index 69d9d6d..1415d90 100644 --- a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/controllers/WorkerController.kt +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/WorkerController.kt @@ -1,13 +1,13 @@ -package ru.touchin.spring.workers.manager.api.controllers +package ru.touchin.spring.workers.manager.api.worker.controllers import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import ru.touchin.spring.workers.manager.api.dto.WorkerDto -import ru.touchin.spring.workers.manager.api.services.WorkerApiService -import ru.touchin.spring.workers.manager.core.services.WorkersStateService +import ru.touchin.spring.workers.manager.api.worker.controllers.dto.WorkerResponse +import ru.touchin.spring.workers.manager.api.worker.services.WorkerApiService +import ru.touchin.spring.workers.manager.core.worker.services.WorkersStateService @RestController @RequestMapping("/workers") @@ -17,7 +17,7 @@ class WorkerController( ) { @GetMapping - fun getWorkers(): List { + fun getWorkers(): List { return workerApiService.getWorkers() } @@ -25,7 +25,7 @@ class WorkerController( fun getWorker( @PathVariable workerName: String - ): WorkerDto { + ): WorkerResponse { return workerApiService.getWorker(workerName) } diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/dto/WorkerResponse.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/dto/WorkerResponse.kt new file mode 100644 index 0000000..eb95964 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/controllers/dto/WorkerResponse.kt @@ -0,0 +1,21 @@ +package ru.touchin.spring.workers.manager.api.worker.controllers.dto + +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import java.time.ZonedDateTime + +data class WorkerResponse( + val workerName: String, + val stoppedAt: ZonedDateTime?, + val disabledAt: ZonedDateTime?, + val status: WorkerStatus, + val parallelExecutionEnabled: Boolean +) + +fun Worker.toWorkerResponse() = WorkerResponse( + workerName = this.name, + stoppedAt = this.stoppedAt, + disabledAt = this.disabledAt, + status = this.status, + parallelExecutionEnabled = this.parallelExecutionEnabled, +) diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiService.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiService.kt new file mode 100644 index 0000000..8268fd3 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiService.kt @@ -0,0 +1,10 @@ +package ru.touchin.spring.workers.manager.api.worker.services + +import ru.touchin.spring.workers.manager.api.worker.controllers.dto.WorkerResponse + +interface WorkerApiService { + + fun getWorker(workerName: String): WorkerResponse + fun getWorkers(): List + +} diff --git a/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiServiceImpl.kt b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiServiceImpl.kt new file mode 100644 index 0000000..99eb6d9 --- /dev/null +++ b/spring-workers-manager-api/src/main/kotlin/ru/touchin/spring/workers/manager/api/worker/services/WorkerApiServiceImpl.kt @@ -0,0 +1,22 @@ +package ru.touchin.spring.workers.manager.api.worker.services + +import org.springframework.stereotype.Service +import ru.touchin.spring.workers.manager.api.worker.controllers.dto.WorkerResponse +import ru.touchin.spring.workers.manager.api.worker.controllers.dto.toWorkerResponse +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService + +@Service +class WorkerApiServiceImpl( + private val workerCoreService: WorkerCoreService +) : WorkerApiService { + + override fun getWorker(workerName: String): WorkerResponse { + return workerCoreService.get(workerName).toWorkerResponse() + } + + override fun getWorkers(): List { + return workerCoreService.getAll().map(Worker::toWorkerResponse) + } + +} diff --git a/spring-workers-manager-core/build.gradle.kts b/spring-workers-manager-core/build.gradle.kts index ebde761..c68c369 100644 --- a/spring-workers-manager-core/build.gradle.kts +++ b/spring-workers-manager-core/build.gradle.kts @@ -5,6 +5,10 @@ plugins { } dependencies { + api(project(":common")) + + implementation(project(":common-spring-jpa")) + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.liquibase:liquibase-core") diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt index 61ee3e5..7cb85cf 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/WorkersManagerConfiguration.kt @@ -8,6 +8,7 @@ import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.data.jpa.repository.config.EnableJpaAuditing import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import ru.touchin.common.spring.jpa.EnableJpaAuditingExtra /** * Configuration which brings to context all the components, required to support workers manager module via annotations. @@ -31,7 +32,7 @@ class WorkersManagerConfiguration { */ @Configuration @ConditionalOnMissingBean(name=["jpaAuditingHandler"]) - @EnableJpaAuditing + @EnableJpaAuditingExtra class JpaAuditingNonConflictingDeclaration } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/converters/ExecutionConverters.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/converters/ExecutionConverters.kt new file mode 100644 index 0000000..8041ec6 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/converters/ExecutionConverters.kt @@ -0,0 +1,15 @@ +package ru.touchin.spring.workers.manager.core.execution.converters + +import ru.touchin.spring.workers.manager.core.execution.dto.Execution +import ru.touchin.spring.workers.manager.core.execution.models.ExecutionEntity +import ru.touchin.spring.workers.manager.core.trigger.converters.toTriggerDescriptor + +fun ExecutionEntity.toExecution(): Execution { + return Execution( + id = id!!, + triggerDescriptor = triggerDescriptor?.toTriggerDescriptor(), + status = status, + startedAt = startedAt, + finishedAt = finishedAt, + ) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/dto/Execution.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/dto/Execution.kt new file mode 100644 index 0000000..521d847 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/dto/Execution.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.core.execution.dto + +import ru.touchin.spring.workers.manager.core.execution.enums.ExecutionStatus +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import java.time.ZonedDateTime +import java.util.* + +data class Execution( + val id: UUID, + val triggerDescriptor: TriggerDescriptor?, + val status: ExecutionStatus, + val startedAt: ZonedDateTime?, + val finishedAt: ZonedDateTime?, +) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/enums/ExecutionStatus.kt similarity index 50% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/enums/ExecutionStatus.kt index 4ad937f..eab45e3 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/ExecutionStatus.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/enums/ExecutionStatus.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.core.models.enums +package ru.touchin.spring.workers.manager.core.execution.enums enum class ExecutionStatus { diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/exceptions/ExecutionNotFoundException.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/exceptions/ExecutionNotFoundException.kt new file mode 100644 index 0000000..acd15fe --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/exceptions/ExecutionNotFoundException.kt @@ -0,0 +1,6 @@ +package ru.touchin.spring.workers.manager.core.execution.exceptions + +import ru.touchin.common.exceptions.CommonNotFoundException +import java.util.* + +class ExecutionNotFoundException(id: UUID) : CommonNotFoundException("Execution not found id=$id") diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/models/ExecutionEntity.kt similarity index 64% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/models/ExecutionEntity.kt index 705c3c5..8e0cea3 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Execution.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/models/ExecutionEntity.kt @@ -1,36 +1,28 @@ -package ru.touchin.spring.workers.manager.core.models +package ru.touchin.spring.workers.manager.core.execution.models -import org.hibernate.annotations.GenericGenerator +import ru.touchin.common.spring.jpa.models.BaseUuidIdEntity import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA -import ru.touchin.spring.workers.manager.core.models.base.BaseEntity -import ru.touchin.spring.workers.manager.core.models.enums.ExecutionStatus +import ru.touchin.spring.workers.manager.core.execution.enums.ExecutionStatus +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity import java.time.ZonedDateTime import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated -import javax.persistence.GeneratedValue -import javax.persistence.Id import javax.persistence.JoinColumn import javax.persistence.ManyToOne import javax.persistence.Table @Entity @Table(name = "executions", schema = SCHEMA) -class Execution : BaseEntity() { - - @Id - @GeneratedValue(generator = "uuid") - @GenericGenerator(name = "uuid", strategy = "uuid2") - @Column(name = "id", unique = true) - var id: String? = null +class ExecutionEntity : BaseUuidIdEntity() { @Column(name = "worker_name", nullable = false) lateinit var workerName: String @ManyToOne @JoinColumn(name = "trigger_id", nullable = true) - var triggerDescriptor: TriggerDescriptor? = null + var triggerDescriptor: TriggerDescriptorEntity? = null @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/repositories/ExecutionRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/repositories/ExecutionRepository.kt new file mode 100644 index 0000000..5cddd4f --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/repositories/ExecutionRepository.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.core.execution.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.repository.findByIdOrNull +import ru.touchin.spring.workers.manager.core.execution.exceptions.ExecutionNotFoundException +import ru.touchin.spring.workers.manager.core.execution.models.ExecutionEntity +import java.util.* + +interface ExecutionRepository : JpaRepository + +fun ExecutionRepository.findByIdOrThrow(id: UUID): ExecutionEntity { + return findByIdOrNull(id) + ?: throw ExecutionNotFoundException(id) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreService.kt new file mode 100644 index 0000000..25ec190 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreService.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.core.execution.services + +import ru.touchin.spring.workers.manager.core.execution.dto.Execution +import ru.touchin.spring.workers.manager.core.execution.services.dto.CreateExecution +import java.util.* + +interface ExecutionCoreService{ + + fun create(create: CreateExecution): Execution + fun setFinished(id: UUID): Execution + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreServiceImpl.kt new file mode 100644 index 0000000..a18d2d2 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/ExecutionCoreServiceImpl.kt @@ -0,0 +1,49 @@ +package ru.touchin.spring.workers.manager.core.execution.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.core.execution.converters.toExecution +import ru.touchin.spring.workers.manager.core.execution.dto.Execution +import ru.touchin.spring.workers.manager.core.execution.enums.ExecutionStatus +import ru.touchin.spring.workers.manager.core.execution.models.ExecutionEntity +import ru.touchin.spring.workers.manager.core.execution.repositories.ExecutionRepository +import ru.touchin.spring.workers.manager.core.execution.repositories.findByIdOrThrow +import ru.touchin.spring.workers.manager.core.execution.services.dto.CreateExecution +import ru.touchin.spring.workers.manager.core.trigger.repositories.TriggerDescriptorRepository +import ru.touchin.spring.workers.manager.core.trigger.repositories.findByIdOrThrow +import java.time.ZonedDateTime +import java.util.* + +@Service +class ExecutionCoreServiceImpl( + private val executionRepository: ExecutionRepository, + private val triggerDescriptorRepository: TriggerDescriptorRepository, +) : ExecutionCoreService { + + @Transactional + override fun create(create: CreateExecution): Execution { + val entity = ExecutionEntity().apply { + startedAt = ZonedDateTime.now() + workerName = create.workerName + triggerDescriptor = triggerDescriptorRepository.findByIdOrThrow(create.triggerId) + status = ExecutionStatus.PROCESSING + } + + return executionRepository.save(entity) + .toExecution() + } + + @Transactional + override fun setFinished(id: UUID): Execution { + val entity = executionRepository.findByIdOrThrow(id) + + entity.apply { + finishedAt = ZonedDateTime.now() + status = ExecutionStatus.FINISHED + } + + return executionRepository.save(entity) + .toExecution() + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/dto/CreateExecution.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/dto/CreateExecution.kt new file mode 100644 index 0000000..d889da5 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/execution/services/dto/CreateExecution.kt @@ -0,0 +1,8 @@ +package ru.touchin.spring.workers.manager.core.execution.services.dto + +import java.util.* + +data class CreateExecution( + val workerName: String, + val triggerId: UUID, +) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt deleted file mode 100644 index ec390ac..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/base/BaseEntity.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.touchin.spring.workers.manager.core.models.base - -import org.springframework.data.annotation.CreatedDate -import org.springframework.data.annotation.LastModifiedDate -import org.springframework.data.jpa.domain.support.AuditingEntityListener -import java.io.Serializable -import java.time.LocalDateTime -import javax.persistence.Column -import javax.persistence.EntityListeners -import javax.persistence.MappedSuperclass - -@EntityListeners(AuditingEntityListener::class) -@MappedSuperclass -abstract class BaseEntity : Serializable { - - @CreatedDate - @Column(name = "created_at", nullable = false) - lateinit var createdAt: LocalDateTime - - @LastModifiedDate - @Column(name = "updated_at", nullable = true) - var updatedAt: LocalDateTime? = null - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt deleted file mode 100644 index 8e72a16..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/WorkerStatus.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ru.touchin.spring.workers.manager.core.models.enums - -enum class WorkerStatus { - - IDLE, - PROCESSING - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt deleted file mode 100644 index 5d2f974..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/ExecutionRepository.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.touchin.spring.workers.manager.core.repositories - -import org.springframework.data.jpa.repository.JpaRepository -import ru.touchin.spring.workers.manager.core.models.Execution - -interface ExecutionRepository : JpaRepository diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt deleted file mode 100644 index 2906710..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/RepositoryUtils.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ru.touchin.spring.workers.manager.core.repositories - -import org.springframework.data.jpa.repository.JpaRepository - -object RepositoryUtils { - - const val SKIP_LOCKED_PARAMETER = "javax.persistence.lock.timeout" - - - /** - * Const value which is used for 'SKIP LOCKED' in 'SELECT ... FOR UPDATE' query - */ - const val SKIP_LOCKED_VALUE = "-2" - -} - - -fun JpaRepository.upsert(entity: T, updater: (T) -> Unit): T { - return save(entity.also(updater)) -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt deleted file mode 100644 index 63f94c8..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/TriggerDescriptorRepository.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.touchin.spring.workers.manager.core.repositories - -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Query -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor - -interface TriggerDescriptorRepository : JpaRepository { - - @Query( - """ - SELECT td - FROM TriggerDescriptor td - WHERE td.worker.workerName = :workerName - AND td.deletedAt IS NULL - """ - ) - fun findAll(workerName: String): List - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt deleted file mode 100644 index fef419c..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/repositories/WorkerRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ru.touchin.spring.workers.manager.core.repositories - -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Lock -import org.springframework.data.jpa.repository.Query -import org.springframework.data.jpa.repository.QueryHints -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.repositories.RepositoryUtils.SKIP_LOCKED_PARAMETER -import ru.touchin.spring.workers.manager.core.repositories.RepositoryUtils.SKIP_LOCKED_VALUE -import javax.persistence.LockModeType -import javax.persistence.QueryHint - -interface WorkerRepository : JpaRepository { - - @Query( - """ - SELECT w - FROM Worker w - WHERE w.workerName = :workerName - """ - ) - @Lock(LockModeType.PESSIMISTIC_WRITE) - fun findWithLock(workerName: String): Worker? - - @Query( - """ - SELECT w - FROM Worker w - WHERE w.workerName = :workerName - """ - ) - fun find(workerName: String): Worker? - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt deleted file mode 100644 index 6195ba3..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/ExecutionService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services - -import ru.touchin.spring.workers.manager.core.models.Execution -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker - -interface ExecutionService{ - - fun finishExecution(execution: Execution) - fun createExecution(worker: Worker, triggerDescriptor: TriggerDescriptor?): Execution - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt deleted file mode 100644 index 3923424..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/TriggerDescriptorCoreService.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services - -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor - -interface TriggerDescriptorCoreService { - - fun save(triggerDescriptor: TriggerDescriptor) - fun delete(triggerDescriptor: TriggerDescriptor) - fun getByWorkerName(workerName: String): List - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt deleted file mode 100644 index 29a6dc0..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkerCoreService.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services - -import ru.touchin.spring.workers.manager.core.models.Worker - -interface WorkerCoreService{ - - fun save(worker: Worker): Worker - fun getByNameWithLock(workerName: String): Worker? - fun get(name: String): Worker = getOrNull(name) ?: throw NoSuchElementException("Worker not found by name $name") - fun getOrNull(name: String): Worker? - fun getAll(): List - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt deleted file mode 100644 index f821bdf..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/ExecutionServiceImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services.impl - -import org.springframework.stereotype.Service -import ru.touchin.spring.workers.manager.core.models.Execution -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.models.enums.ExecutionStatus -import ru.touchin.spring.workers.manager.core.repositories.ExecutionRepository -import ru.touchin.spring.workers.manager.core.repositories.upsert -import ru.touchin.spring.workers.manager.core.services.ExecutionService -import java.time.ZonedDateTime - -@Service -class ExecutionServiceImpl( - private val executionRepository: ExecutionRepository -) : ExecutionService { - - override fun createExecution(worker: Worker, triggerDescriptor: TriggerDescriptor?): Execution { - return executionRepository.upsert(Execution()) { - it.startedAt = ZonedDateTime.now() - it.workerName = worker.workerName - it.triggerDescriptor = triggerDescriptor - it.status = ExecutionStatus.PROCESSING - } - } - - override fun finishExecution(execution: Execution) { - executionRepository.upsert(execution) { - it.finishedAt = ZonedDateTime.now() - it.status = ExecutionStatus.FINISHED - } - } - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt deleted file mode 100644 index cfa2939..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/TriggerDescriptorCoreServiceImpl.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services.impl - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.core.models.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.repositories.TriggerDescriptorRepository -import ru.touchin.spring.workers.manager.core.repositories.upsert -import ru.touchin.spring.workers.manager.core.services.TriggerDescriptorCoreService -import java.time.ZonedDateTime - -@Service -class TriggerDescriptorCoreServiceImpl( - private val triggerDescriptorRepository: TriggerDescriptorRepository -) : TriggerDescriptorCoreService { - - @Transactional - override fun save(triggerDescriptor: TriggerDescriptor) { - triggerDescriptorRepository.save(triggerDescriptor) - } - - @Transactional - override fun delete(triggerDescriptor: TriggerDescriptor) { - triggerDescriptorRepository.upsert(triggerDescriptor) { - it.deletedAt = ZonedDateTime.now() - } - } - - override fun getByWorkerName(workerName: String): List { - return triggerDescriptorRepository.findAll(workerName) - } - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt deleted file mode 100644 index 91f54af..0000000 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerCoreServiceImpl.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ru.touchin.spring.workers.manager.core.services.impl - -import org.springframework.stereotype.Service -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository -import ru.touchin.spring.workers.manager.core.services.WorkerCoreService - -@Service -class WorkerCoreServiceImpl( - private val workerRepository: WorkerRepository -) : WorkerCoreService { - - override fun save(worker: Worker): Worker { - return workerRepository.save(worker) - } - - override fun getByNameWithLock(workerName: String): Worker? { - return workerRepository.findWithLock(workerName) - } - - override fun getOrNull(name: String): Worker? = workerRepository.find(name) - - override fun getAll(): List = workerRepository.findAll() - -} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/converters/TriggerDescriptorConverters.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/converters/TriggerDescriptorConverters.kt new file mode 100644 index 0000000..a6e5893 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/converters/TriggerDescriptorConverters.kt @@ -0,0 +1,16 @@ +package ru.touchin.spring.workers.manager.core.trigger.converters + +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity + +fun TriggerDescriptorEntity.toTriggerDescriptor(): TriggerDescriptor { + return TriggerDescriptor( + id = id!!, + name = triggerName, + type = type, + expression = expression, + workerName = worker.workerName, + disabledAt = disabledAt, + deletedAt = deletedAt, + ) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt new file mode 100644 index 0000000..1b8fc27 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt @@ -0,0 +1,31 @@ +package ru.touchin.spring.workers.manager.core.trigger.dto + +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import java.time.ZonedDateTime +import java.util.* + +data class TriggerDescriptor( + val id: UUID, + val name: String, + val type: TriggerType, + val expression: String, + val workerName: String, + val disabledAt: ZonedDateTime?, + val deletedAt: ZonedDateTime?, +) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is TriggerDescriptorEntity) return false + + if (id != other.id) return false + + return true + } + + override fun hashCode(): Int { + return id.hashCode() + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/enums/TriggerType.kt similarity index 95% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/enums/TriggerType.kt index 3dbb0b3..4a6fd27 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/enums/TriggerType.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/enums/TriggerType.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.core.models.enums +package ru.touchin.spring.workers.manager.core.trigger.enums enum class TriggerType { @@ -37,11 +37,13 @@ enum class TriggerType { FIXED_RATE; companion object { + fun find(name: String?): TriggerType? { name ?: return null return values().find { it.name == name } } + } } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/exceptions/TriggerNotFoundException.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/exceptions/TriggerNotFoundException.kt new file mode 100644 index 0000000..cc8bd87 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/exceptions/TriggerNotFoundException.kt @@ -0,0 +1,11 @@ +package ru.touchin.spring.workers.manager.core.trigger.exceptions + +import ru.touchin.common.exceptions.CommonNotFoundException +import java.util.* + +class TriggerNotFoundException : CommonNotFoundException { + + constructor(id: UUID) : super("TriggerDescriptor not found id=$id") + constructor(name: String) : super("TriggerDescriptor code not found name=$name") + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt similarity index 64% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt index 46d15d3..fa85fa2 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/TriggerDescriptor.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt @@ -1,9 +1,11 @@ -package ru.touchin.spring.workers.manager.core.models +package ru.touchin.spring.workers.manager.core.trigger.models import org.hibernate.annotations.GenericGenerator +import ru.touchin.common.spring.jpa.models.BaseEntity +import ru.touchin.common.spring.jpa.models.BaseUuidIdEntity import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA -import ru.touchin.spring.workers.manager.core.models.base.BaseEntity -import ru.touchin.spring.workers.manager.core.models.enums.TriggerType +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity import java.time.ZonedDateTime import javax.persistence.Column import javax.persistence.Entity @@ -17,13 +19,7 @@ import javax.persistence.Table @Entity @Table(name = "triggers", schema = SCHEMA) -class TriggerDescriptor : BaseEntity() { - - @Id - @GeneratedValue(generator = "uuid") - @GenericGenerator(name = "uuid", strategy = "uuid2") - @Column(name = "id", unique = true) - var id: String? = null +class TriggerDescriptorEntity : BaseUuidIdEntity() { /** * This field is **immutable** @@ -43,7 +39,7 @@ class TriggerDescriptor : BaseEntity() { */ @ManyToOne @JoinColumn(name = "worker_name", nullable = false) - lateinit var worker: Worker + lateinit var worker: WorkerEntity /** * This field is **immutable** @@ -59,17 +55,4 @@ class TriggerDescriptor : BaseEntity() { fun isDisabled() = disabledAt != null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is TriggerDescriptor) return false - - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - return id?.hashCode() ?: 0 - } - } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/repositories/TriggerDescriptorRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/repositories/TriggerDescriptorRepository.kt new file mode 100644 index 0000000..7f33d69 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/repositories/TriggerDescriptorRepository.kt @@ -0,0 +1,27 @@ +package ru.touchin.spring.workers.manager.core.trigger.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.findByIdOrNull +import ru.touchin.spring.workers.manager.core.trigger.exceptions.TriggerNotFoundException +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import java.util.* + +interface TriggerDescriptorRepository : JpaRepository { + + @Query( + """ + SELECT td + FROM TriggerDescriptorEntity td + WHERE td.worker.workerName = :workerName + AND td.deletedAt IS NULL + """ + ) + fun findAll(workerName: String): List + +} + +fun TriggerDescriptorRepository.findByIdOrThrow(id: UUID): TriggerDescriptorEntity { + return findByIdOrNull(id) + ?: throw TriggerNotFoundException(id) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreService.kt new file mode 100644 index 0000000..7e2c18d --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreService.kt @@ -0,0 +1,14 @@ +package ru.touchin.spring.workers.manager.core.trigger.services + +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import java.util.* + +interface TriggerDescriptorCoreService { + + fun create(create: CreateTriggerDescriptor): TriggerDescriptor + fun create(create: List): List + fun setDeleted(id: UUID): TriggerDescriptor + fun getByWorkerName(workerName: String): List + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreServiceImpl.kt new file mode 100644 index 0000000..2b20183 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/TriggerDescriptorCoreServiceImpl.kt @@ -0,0 +1,68 @@ +package ru.touchin.spring.workers.manager.core.trigger.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.core.trigger.converters.toTriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import ru.touchin.spring.workers.manager.core.trigger.repositories.TriggerDescriptorRepository +import ru.touchin.spring.workers.manager.core.trigger.repositories.findByIdOrThrow +import ru.touchin.spring.workers.manager.core.trigger.services.dto.CreateTriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.repositories.WorkerRepository +import ru.touchin.spring.workers.manager.core.worker.repositories.findByNameOrThrow +import java.time.ZonedDateTime +import java.util.* + +@Service +class TriggerDescriptorCoreServiceImpl( + private val triggerDescriptorRepository: TriggerDescriptorRepository, + private val workerRepository: WorkerRepository, +) : TriggerDescriptorCoreService { + + @Transactional + override fun create(create: CreateTriggerDescriptor): TriggerDescriptor { + val entity = TriggerDescriptorEntity().apply { + expression = create.expression + triggerName = create.name + type = create.type + worker = workerRepository.findByNameOrThrow(create.workerName) + disabledAt = create.disabledAt + } + + return triggerDescriptorRepository.save(entity) + .toTriggerDescriptor() + } + + @Transactional + override fun create(create: List): List { + val entities = create.map { dto -> + TriggerDescriptorEntity().apply { + expression = dto.expression + triggerName = dto.name + type = dto.type + worker = workerRepository.findByNameOrThrow(dto.workerName) + disabledAt = dto.disabledAt + } + } + + return triggerDescriptorRepository.saveAll(entities) + .map(TriggerDescriptorEntity::toTriggerDescriptor) + } + + @Transactional + override fun setDeleted(id: UUID): TriggerDescriptor { + val entity = triggerDescriptorRepository.findByIdOrThrow(id) + + entity.apply { deletedAt = ZonedDateTime.now() } + + return triggerDescriptorRepository.save(entity) + .toTriggerDescriptor() + } + + @Transactional(readOnly = true) + override fun getByWorkerName(workerName: String): List { + return triggerDescriptorRepository.findAll(workerName) + .map(TriggerDescriptorEntity::toTriggerDescriptor) + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/dto/CreateTriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/dto/CreateTriggerDescriptor.kt new file mode 100644 index 0000000..2f3f545 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/services/dto/CreateTriggerDescriptor.kt @@ -0,0 +1,12 @@ +package ru.touchin.spring.workers.manager.core.trigger.services.dto + +import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType +import java.time.ZonedDateTime + +data class CreateTriggerDescriptor( + val name: String, + val type: TriggerType, + val expression: String, + val disabledAt: ZonedDateTime?, + val workerName: String, +) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/converters/WorkerConverters.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/converters/WorkerConverters.kt new file mode 100644 index 0000000..5be2895 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/converters/WorkerConverters.kt @@ -0,0 +1,16 @@ +package ru.touchin.spring.workers.manager.core.worker.converters + +import ru.touchin.spring.workers.manager.core.trigger.converters.toTriggerDescriptor +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity + +fun WorkerEntity.toWorker(): Worker { + return Worker( + name = workerName, + stoppedAt = stoppedAt, + disabledAt = disabledAt, + status = status, + parallelExecutionEnabled = parallelExecutionEnabled, + ) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt new file mode 100644 index 0000000..d81f299 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt @@ -0,0 +1,23 @@ +package ru.touchin.spring.workers.manager.core.worker.dto + +import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import java.time.ZonedDateTime + +data class Worker( + val name: String, + val stoppedAt: ZonedDateTime?, + val disabledAt: ZonedDateTime?, + val status: WorkerStatus, + val parallelExecutionEnabled: Boolean, +) { + + fun isStopped() = stoppedAt != null + + fun isDisabled() = disabledAt != null + +// fun getEnabledTriggerDescriptors() = triggerDescriptors.filter { it.disabledAt == null && it.deletedAt == null } +// +// fun getAllTriggerDescriptors() = triggerDescriptors.filter { it.deletedAt == null } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/enums/WorkerStatus.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/enums/WorkerStatus.kt new file mode 100644 index 0000000..e047d24 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/enums/WorkerStatus.kt @@ -0,0 +1,8 @@ +package ru.touchin.spring.workers.manager.core.worker.enums + +enum class WorkerStatus { + + IDLE, + PROCESSING + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/exceptions/WorkerNotFoundException.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/exceptions/WorkerNotFoundException.kt new file mode 100644 index 0000000..8938362 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/exceptions/WorkerNotFoundException.kt @@ -0,0 +1,7 @@ +package ru.touchin.spring.workers.manager.core.worker.exceptions + +import ru.touchin.common.exceptions.CommonNotFoundException + +class WorkerNotFoundException(name: String): CommonNotFoundException( + "Worker not found name=$name" +) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/models/WorkerEntity.kt similarity index 65% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/models/WorkerEntity.kt index e5c6467..19f9c90 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/models/Worker.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/models/WorkerEntity.kt @@ -1,8 +1,9 @@ -package ru.touchin.spring.workers.manager.core.models +package ru.touchin.spring.workers.manager.core.worker.models +import ru.touchin.common.spring.jpa.models.BaseEntity import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA -import ru.touchin.spring.workers.manager.core.models.base.BaseEntity -import ru.touchin.spring.workers.manager.core.models.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus import java.time.ZonedDateTime import javax.persistence.Column import javax.persistence.Entity @@ -15,7 +16,7 @@ import javax.persistence.Table @Entity @Table(name = "workers", schema = SCHEMA) -class Worker : BaseEntity() { +class WorkerEntity : BaseEntity() { @Id @Column(name = "worker_name", unique = true) @@ -35,14 +36,6 @@ class Worker : BaseEntity() { var parallelExecutionEnabled: Boolean = false @OneToMany(mappedBy = "worker", orphanRemoval = true, fetch = FetchType.LAZY) - lateinit var triggerDescriptors: Set - - fun isStopped() = stoppedAt != null - - fun isDisabled() = disabledAt != null - - fun getEnabledTriggerDescriptors() = triggerDescriptors.filter { !it.isDisabled() && it.deletedAt == null} - - fun getAllTriggerDescriptors() = triggerDescriptors.filter { it.deletedAt == null} + lateinit var triggerDescriptors: Set } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/repositories/WorkerRepository.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/repositories/WorkerRepository.kt new file mode 100644 index 0000000..4fb3f0a --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/repositories/WorkerRepository.kt @@ -0,0 +1,36 @@ +package ru.touchin.spring.workers.manager.core.worker.repositories + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Lock +import org.springframework.data.jpa.repository.Query +import ru.touchin.spring.workers.manager.core.worker.exceptions.WorkerNotFoundException +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity +import javax.persistence.LockModeType + +interface WorkerRepository : JpaRepository { + + @Query( + """ + SELECT w + FROM WorkerEntity w + WHERE w.workerName = :workerName + """ + ) + @Lock(LockModeType.PESSIMISTIC_WRITE) + fun findWithLock(workerName: String): WorkerEntity? + + @Query( + """ + SELECT w + FROM WorkerEntity w + WHERE w.workerName = :workerName + """ + ) + fun findByName(workerName: String): WorkerEntity? + +} + +fun WorkerRepository.findByNameOrThrow(name: String): WorkerEntity { + return findByName(name) + ?: throw WorkerNotFoundException(name) +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreService.kt new file mode 100644 index 0000000..7739b6b --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreService.kt @@ -0,0 +1,17 @@ +package ru.touchin.spring.workers.manager.core.worker.services + +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.exceptions.WorkerNotFoundException +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity +import ru.touchin.spring.workers.manager.core.worker.services.dto.UpdateWorker + +interface WorkerCoreService { + + fun create(name: String): Worker + fun update(update: UpdateWorker): Worker + fun getWithLock(name: String): Worker? + fun get(name: String): Worker = getOrNull(name) ?: throw WorkerNotFoundException(name) + fun getOrNull(name: String): Worker? + fun getAll(): List + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreServiceImpl.kt new file mode 100644 index 0000000..b7d063c --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerCoreServiceImpl.kt @@ -0,0 +1,56 @@ +package ru.touchin.spring.workers.manager.core.worker.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ru.touchin.spring.workers.manager.core.worker.converters.toWorker +import ru.touchin.spring.workers.manager.core.worker.dto.Worker +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity +import ru.touchin.spring.workers.manager.core.worker.repositories.WorkerRepository +import ru.touchin.spring.workers.manager.core.worker.repositories.findByNameOrThrow +import ru.touchin.spring.workers.manager.core.worker.services.dto.UpdateWorker + +@Service +class WorkerCoreServiceImpl( + private val workerRepository: WorkerRepository, +) : WorkerCoreService { + + @Transactional + override fun create(name: String): Worker { + val entity = WorkerEntity().apply { + workerName = name + status = WorkerStatus.IDLE + } + + return workerRepository.save(entity) + .toWorker() + } + + @Transactional + override fun update(update: UpdateWorker): Worker { + val entity = workerRepository.findByNameOrThrow(update.name) + .apply { status = update.status } + + return workerRepository.save(entity) + .toWorker() + } + + @Transactional + override fun getWithLock(name: String): Worker? { + return workerRepository.findWithLock(name) + ?.toWorker() + } + + @Transactional(readOnly = true) + override fun getOrNull(name: String): Worker? { + return workerRepository.findByName(name) + ?.toWorker() + } + + @Transactional(readOnly = true) + override fun getAll(): List { + return workerRepository.findAll() + .map(WorkerEntity::toWorker) + } + +} diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerStateServiceImpl.kt similarity index 72% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerStateServiceImpl.kt index 023287c..0f02056 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/impl/WorkerStateServiceImpl.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkerStateServiceImpl.kt @@ -1,12 +1,10 @@ -package ru.touchin.spring.workers.manager.core.services.impl +package ru.touchin.spring.workers.manager.core.worker.services import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import ru.touchin.spring.workers.manager.core.models.Worker -import ru.touchin.spring.workers.manager.core.repositories.WorkerRepository -import ru.touchin.spring.workers.manager.core.repositories.upsert -import ru.touchin.spring.workers.manager.core.services.WorkersStateService +import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity +import ru.touchin.spring.workers.manager.core.worker.repositories.WorkerRepository import java.time.ZonedDateTime @Service @@ -42,11 +40,12 @@ class WorkerStateServiceImpl( } } - private fun updateWorkerWithLock(name: String, updater: (Worker) -> Unit) { + private fun updateWorkerWithLock(name: String, updater: (WorkerEntity) -> Unit) { val worker = workerRepository.findWithLock(name) + ?.apply(updater) ?: throw NoSuchElementException("Worker with name $name not found") - workerRepository.upsert(worker, updater) + workerRepository.save(worker) } } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkersStateService.kt similarity index 70% rename from spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt rename to spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkersStateService.kt index 7dfe992..46fd10b 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/services/WorkersStateService.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/WorkersStateService.kt @@ -1,4 +1,4 @@ -package ru.touchin.spring.workers.manager.core.services +package ru.touchin.spring.workers.manager.core.worker.services interface WorkersStateService { diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/dto/UpdateWorker.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/dto/UpdateWorker.kt new file mode 100644 index 0000000..899e4e2 --- /dev/null +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/services/dto/UpdateWorker.kt @@ -0,0 +1,8 @@ +package ru.touchin.spring.workers.manager.core.worker.services.dto + +import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus + +data class UpdateWorker( + val name: String, + val status: WorkerStatus, +) diff --git a/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml index 3510e11..facd751 100644 --- a/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml +++ b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml @@ -58,7 +58,7 @@ databaseChangeLog: columns: - column: name: id - type: VARCHAR(250) + type: UUID constraints: nullable: false primaryKey: true @@ -114,7 +114,7 @@ databaseChangeLog: columns: - column: name: id - type: VARCHAR(250) + type: UUID constraints: nullable: false primaryKey: true diff --git a/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml b/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml new file mode 100644 index 0000000..40fc4f3 --- /dev/null +++ b/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test-slow.yml" diff --git a/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml b/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml new file mode 100644 index 0000000..956da22 --- /dev/null +++ b/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml @@ -0,0 +1,3 @@ +spring: + config: + import: "test.yml" From 7b1a7cfc5ef5deb7f811fbb02840ce8396a0b0ba Mon Sep 17 00:00:00 2001 From: Mikhail Yasnov Date: Fri, 3 Dec 2021 12:00:36 +0300 Subject: [PATCH 3/5] Fix tests and updating trigger expression --- .../agent/registry/JobDefinitionsRegistry.kt | 5 +-- .../manager/{agent => }/TestApplication.kt | 4 +- .../manager/agent/common/utils/GlobTest.kt | 2 +- .../agent/config/WorkerInitializerTest.kt | 43 ++++++------------- .../scheduled/WorkerManagerWatcherTest.kt | 40 +++++++++-------- .../core/trigger/dto/TriggerDescriptor.kt | 7 +-- .../trigger/models/TriggerDescriptorEntity.kt | 18 -------- .../db/changelog/db.changelog-master.yaml | 2 +- .../resources/application-test-slow.yml | 3 -- .../kotlin/resources/application-test.yml | 3 -- 10 files changed, 43 insertions(+), 84 deletions(-) rename spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/{agent => }/TestApplication.kt (62%) delete mode 100644 spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml delete mode 100644 spring-workers-manager-core/src/test/kotlin/resources/application-test.yml diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt index 3b2b5ba..6e1c848 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/registry/JobDefinitionsRegistry.kt @@ -24,10 +24,7 @@ class JobDefinitionsRegistry( final val jobNames: Set init { - val allJobs = providers.flatMap { - val job =it.getJobs() - job - } + val allJobs = providers.flatMap(JobProvider::getJobs) val name2jobsList = LinkedMultiValueMap() diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/TestApplication.kt similarity index 62% rename from spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt rename to spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/TestApplication.kt index acc6024..02e2fc2 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/TestApplication.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/TestApplication.kt @@ -1,10 +1,8 @@ -package ru.touchin.spring.workers.manager.agent +package ru.touchin.spring.workers.manager import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.test.context.TestConfiguration -import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Import -import ru.touchin.spring.workers.manager.WorkersManagerConfiguration @SpringBootApplication @TestConfiguration diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt index 615952a..8ed5fc4 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/common/utils/GlobTest.kt @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test -class GlobTest { +internal class GlobTest { @Test fun matches() { diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt index 9677ebd..9c70063 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/config/WorkerInitializerTest.kt @@ -1,20 +1,16 @@ package ru.touchin.spring.workers.manager.agent.config import com.nhaarman.mockitokotlin2.doNothing -import com.nhaarman.mockitokotlin2.doReturn -import org.junit.jupiter.api.BeforeEach +import com.nhaarman.mockitokotlin2.mock import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.`when` import org.mockito.Mockito.doAnswer import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.springframework.test.util.ReflectionTestUtils import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry -import ru.touchin.spring.workers.manager.agent.registry.SimpleJobProvider +import ru.touchin.spring.workers.manager.agent.registry.JobProvider import ru.touchin.spring.workers.manager.agent.trigger.services.TriggerDescriptorService import ru.touchin.spring.workers.manager.agent.worker.executors.WorkerActionExecutorImpl import ru.touchin.spring.workers.manager.core.worker.dto.Worker @@ -23,12 +19,15 @@ import ru.touchin.spring.workers.manager.core.worker.services.WorkerCoreService import ru.touchin.spring.workers.manager.core.worker.services.WorkersStateService import ru.touchin.spring.workers.manager.utils.MockitoUtils.anyx -class WorkerInitializerTest { +internal class WorkerInitializerTest { - private val simpleJobProvider = Mockito.mock(SimpleJobProvider::class.java) - - private val baseJob = Mockito.mock(BaseJob::class.java) + private val baseJob = mock { + on(it.getName()).thenReturn(BASE_WORKER_NAME) + } + private val simpleJobProvider = mock { + on(it.getJobs()).thenReturn(listOf(baseJob)) + } private val workerActionExecutor = Mockito.mock(WorkerActionExecutorImpl::class.java) @@ -54,29 +53,10 @@ class WorkerInitializerTest { parallelExecutionEnabled = false, ) -// private val baseJob = object : BaseJob { -// override fun run(){} -// -// override fun getName() = BASE_WORKER_NAME -// } - - @BeforeEach - fun setUp() { - doAnswer { BASE_WORKER_NAME }.`when`(baseJob).getName() - `when`(simpleJobProvider.jobBeans).doReturn(listOf(baseJob)) - doAnswer { listOf(baseJob) }.`when`(simpleJobProvider).getJobs() - -// doAnswer { mapOf(BASE_WORKER_NAME to baseWorker) } -// .`when`(jobDefinitionsRegistry).jobs - } - @Test - fun init_existingWorker() { + fun checkSyncWithExistingWorker() { doAnswer { baseWorker }.`when`(workerCoreService).getWithLock(BASE_WORKER_NAME) doNothing().`when`(workersStateService).start(anyx()) -// val jobs = - doAnswer { listOf(baseJob) }.`when`(simpleJobProvider).getJobs() - workerInitializer.init() @@ -86,7 +66,7 @@ class WorkerInitializerTest { } @Test - fun init_newWorker() { + fun checkSyncAndCreateNewWorker() { doAnswer { null }.`when`(workerCoreService).getWithLock(BASE_WORKER_NAME) doAnswer { baseWorker }.`when`(workerCoreService).create(BASE_WORKER_NAME) @@ -99,6 +79,7 @@ class WorkerInitializerTest { companion object { private const val BASE_WORKER_NAME = "baseWorker" + } } diff --git a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt index 6667176..05a6efa 100644 --- a/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt +++ b/spring-workers-manager-agent/src/test/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcherTest.kt @@ -1,7 +1,6 @@ package ru.touchin.spring.workers.manager.agent.scheduled -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers @@ -10,12 +9,12 @@ import org.mockito.Mockito.anyString import org.mockito.Mockito.doAnswer import org.mockito.Mockito.verify import org.quartz.Scheduler -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ActiveProfiles import ru.touchin.spring.workers.manager.agent.common.base.BaseJob import ru.touchin.spring.workers.manager.agent.registry.JobDefinitionsRegistry +import ru.touchin.spring.workers.manager.agent.registry.JobProvider import ru.touchin.spring.workers.manager.agent.registry.TriggersRegistry import ru.touchin.spring.workers.manager.agent.scheduled.services.SchedulerServiceImpl +import ru.touchin.spring.workers.manager.agent.worker.executors.WorkerActionExecutorImpl import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType import ru.touchin.spring.workers.manager.core.trigger.services.TriggerDescriptorCoreService @@ -23,11 +22,20 @@ import ru.touchin.spring.workers.manager.core.worker.dto.Worker import ru.touchin.spring.workers.manager.core.worker.enums.WorkerStatus import java.util.* -class WorkerManagerWatcherTest { +internal class WorkerManagerWatcherTest { - private val testJob = Mockito.mock(BaseJob::class.java) + private val baseJob = mock { + on(it.getName()).thenReturn(BASE_WORKER_NAME) + } + + private val simpleJobProvider = mock { + on(it.getJobs()).thenReturn(listOf(baseJob)) + } + + private val workerActionExecutor = Mockito.mock(WorkerActionExecutorImpl::class.java) + + private val jobDefinitionsRegistry = JobDefinitionsRegistry(setOf(BASE_WORKER_NAME), listOf(simpleJobProvider), workerActionExecutor) - private val jobDefinitionsRegistry = Mockito.mock(JobDefinitionsRegistry::class.java) private val triggerRegistry = Mockito.mock(TriggersRegistry::class.java) private val watcherSyncInterval = 1000L @@ -62,16 +70,13 @@ class WorkerManagerWatcherTest { @BeforeEach fun setUp() { - doReturn(currentTriggers) + doAnswer { currentTriggers } .`when`(triggerRegistry).getDescriptors() - - doReturn(mapOf(BASE_WORKER_NAME to testJob)) - .`when`(jobDefinitionsRegistry).jobs } @Test - fun check_onlyNewTriggersWithoutRemoving() { - doAnswer {setOf(currentTriggerDescriptor1, actualTriggerDescriptor1)} + fun checkSyncWithOnlyNewTriggersWithoutRemoving() { + doAnswer { listOf(currentTriggerDescriptor1, actualTriggerDescriptor1) } .`when`(triggerDescriptorCoreService).getByWorkerName(anyString()) workerManagerWatcher.sync() @@ -83,8 +88,8 @@ class WorkerManagerWatcherTest { } @Test - fun check_onlyRemoveIrrelevantTriggers() { - doAnswer { emptySet() } + fun checkSyncWithOnlyRemoveIrrelevantTriggers() { + doAnswer { emptyList() } .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) workerManagerWatcher.sync() @@ -96,8 +101,8 @@ class WorkerManagerWatcherTest { } @Test - fun check_saveNewAndRemoveIrrelevantTriggers() { - doAnswer { setOf(actualTriggerDescriptor2, actualTriggerDescriptor1) } + fun checkSyncWithSaveNewAndRemoveIrrelevantTriggers() { + doAnswer { listOf(actualTriggerDescriptor2, actualTriggerDescriptor1) } .`when`(triggerDescriptorCoreService).getByWorkerName(ArgumentMatchers.anyString()) workerManagerWatcher.sync() @@ -123,6 +128,7 @@ class WorkerManagerWatcherTest { companion object { private const val BASE_WORKER_NAME = "baseWorker" + } } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt index 1b8fc27..b443064 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt @@ -1,7 +1,6 @@ package ru.touchin.spring.workers.manager.core.trigger.dto import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType -import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity import java.time.ZonedDateTime import java.util.* @@ -17,9 +16,11 @@ data class TriggerDescriptor( override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is TriggerDescriptorEntity) return false + if (other !is TriggerDescriptor) return false - if (id != other.id) return false + if (id != other.id || expression != other.expression || type != other.type) { + return false + } return true } diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt index fa85fa2..a149b1f 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/models/TriggerDescriptorEntity.kt @@ -1,7 +1,5 @@ package ru.touchin.spring.workers.manager.core.trigger.models -import org.hibernate.annotations.GenericGenerator -import ru.touchin.common.spring.jpa.models.BaseEntity import ru.touchin.common.spring.jpa.models.BaseUuidIdEntity import ru.touchin.spring.workers.manager.WorkersManagerConfiguration.Companion.SCHEMA import ru.touchin.spring.workers.manager.core.trigger.enums.TriggerType @@ -11,8 +9,6 @@ import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated -import javax.persistence.GeneratedValue -import javax.persistence.Id import javax.persistence.JoinColumn import javax.persistence.ManyToOne import javax.persistence.Table @@ -21,29 +17,17 @@ import javax.persistence.Table @Table(name = "triggers", schema = SCHEMA) class TriggerDescriptorEntity : BaseUuidIdEntity() { - /** - * This field is **immutable** - */ @Column(name = "trigger_name", nullable = false) lateinit var triggerName: String - /** - * This field is **immutable** - */ @Enumerated(EnumType.STRING) @Column(name = "type", nullable = false) lateinit var type: TriggerType - /** - * This field is **immutable** - */ @ManyToOne @JoinColumn(name = "worker_name", nullable = false) lateinit var worker: WorkerEntity - /** - * This field is **immutable** - */ @Column(name = "expression", nullable = false) lateinit var expression: String @@ -53,6 +37,4 @@ class TriggerDescriptorEntity : BaseUuidIdEntity() { @Column(name = "deleted_at", nullable = true) var deletedAt: ZonedDateTime? = null - fun isDisabled() = disabledAt != null - } diff --git a/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml index facd751..0ad2e94 100644 --- a/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml +++ b/spring-workers-manager-core/src/main/resources/workers/db/changelog/db.changelog-master.yaml @@ -126,7 +126,7 @@ databaseChangeLog: nullable: false - column: name: trigger_id - type: VARCHAR(250) + type: UUID - column: constraints: nullable: false diff --git a/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml b/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml deleted file mode 100644 index 40fc4f3..0000000 --- a/spring-workers-manager-core/src/test/kotlin/resources/application-test-slow.yml +++ /dev/null @@ -1,3 +0,0 @@ -spring: - config: - import: "test-slow.yml" diff --git a/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml b/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml deleted file mode 100644 index 956da22..0000000 --- a/spring-workers-manager-core/src/test/kotlin/resources/application-test.yml +++ /dev/null @@ -1,3 +0,0 @@ -spring: - config: - import: "test.yml" From 03e06766935415c95d5ad5f79eb87310ca0f22f1 Mon Sep 17 00:00:00 2001 From: Mikhail Yasnov Date: Fri, 3 Dec 2021 12:03:24 +0300 Subject: [PATCH 4/5] Fix README.md --- spring-workers-manager-agent/README.md | 2 +- .../manager/agent/trigger/services/TriggerDescriptorService.kt | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-workers-manager-agent/README.md b/spring-workers-manager-agent/README.md index bc18f65..0d91ad8 100644 --- a/spring-workers-manager-agent/README.md +++ b/spring-workers-manager-agent/README.md @@ -18,7 +18,7 @@ Used as manageable alternative for Spring's `@Scheduled`. class MyJob { @ScheduledAction - @DefaultTrigger(type = "CRON", expression = "0 15 * * * ?") + @InitTrigger(type = "CRON", expression = "0 15 * * * ?") fun sayHello(){ println("Hello, world!") } diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt index 1d2bb58..b90a84b 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/trigger/services/TriggerDescriptorService.kt @@ -1,10 +1,7 @@ package ru.touchin.spring.workers.manager.agent.trigger.services import ru.touchin.spring.workers.manager.core.trigger.dto.TriggerDescriptor -import ru.touchin.spring.workers.manager.core.trigger.models.TriggerDescriptorEntity import ru.touchin.spring.workers.manager.core.worker.dto.Worker -import ru.touchin.spring.workers.manager.core.worker.models.WorkerEntity -import java.time.ZonedDateTime interface TriggerDescriptorService { From 5384dd34302eb634a08f31f2fc189ed38cf62452 Mon Sep 17 00:00:00 2001 From: Mikhail Yasnov Date: Fri, 3 Dec 2021 18:45:16 +0300 Subject: [PATCH 5/5] Add isDisabled method --- .../workers/manager/agent/scheduled/WorkerManagerWatcher.kt | 2 +- .../workers/manager/core/trigger/dto/TriggerDescriptor.kt | 4 ++++ .../touchin/spring/workers/manager/core/worker/dto/Worker.kt | 4 ---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt index dc2a5df..957f78b 100644 --- a/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt +++ b/spring-workers-manager-agent/src/main/kotlin/ru/touchin/spring/workers/manager/agent/scheduled/WorkerManagerWatcher.kt @@ -49,7 +49,7 @@ class WorkerManagerWatcher( val actualTriggerDescriptors = jobDefinitionsRegistry.jobs .flatMap { (jobName, _) -> triggerDescriptorCoreService.getByWorkerName(jobName) } - .filter { it.disabledAt == null } + .filter { !it.isDisabled() } val deletedTriggerDescriptors = currentTriggerDescriptors - actualTriggerDescriptors.toSet() scheduleTriggerService.unscheduleTriggers(deletedTriggerDescriptors) diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt index b443064..0111bd8 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/trigger/dto/TriggerDescriptor.kt @@ -14,6 +14,10 @@ data class TriggerDescriptor( val deletedAt: ZonedDateTime?, ) { + fun isDeleted() = deletedAt != null + + fun isDisabled() = disabledAt != null + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is TriggerDescriptor) return false diff --git a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt index d81f299..4421be4 100644 --- a/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt +++ b/spring-workers-manager-core/src/main/kotlin/ru/touchin/spring/workers/manager/core/worker/dto/Worker.kt @@ -16,8 +16,4 @@ data class Worker( fun isDisabled() = disabledAt != null -// fun getEnabledTriggerDescriptors() = triggerDescriptors.filter { it.disabledAt == null && it.deletedAt == null } -// -// fun getAllTriggerDescriptors() = triggerDescriptors.filter { it.deletedAt == null } - }