From 2f242cf85ae10e5638452fcb3261e7307785fc89 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 08:32:48 +0000 Subject: [PATCH 01/19] refactor: wi --- CLAUDE.md | 42 +++- .../AbstractTemplateExecutableTask.java | 72 ++++++ .../SimpleTemplateExecutableTask.java | 111 +++++++++ .../SteppableTemplateExecutableTask.java | 130 +++++++++++ .../executable/TemplateExecutableTask.java | 220 ------------------ .../builder/ExecutableTaskBuilder.java | 4 +- .../TemplateExecutableTaskBuilder.java | 107 ++++----- ...java => AbstractTemplateLoadedChange.java} | 60 ++--- .../loaded/SimpleTemplateLoadedChange.java | 61 +++++ .../loaded/SteppableTemplateLoadedChange.java | 54 +++++ .../loaded/TemplateLoadedTaskBuilder.java | 59 +++-- .../SimpleTemplateLoadedTaskBuilderTest.java | 9 +- ...teppableTemplateLoadedTaskBuilderTest.java | 42 ++-- .../graalvm/RegistrationFeature.java | 12 +- .../springboot/SpringbootProfileFilter.java | 8 +- 15 files changed, 624 insertions(+), 367 deletions(-) create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java delete mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/TemplateExecutableTask.java rename core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/{TemplateLoadedChange.java => AbstractTemplateLoadedChange.java} (65%) create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java diff --git a/CLAUDE.md b/CLAUDE.md index 593f290f3..1994e26e9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -338,11 +338,15 @@ YAML File ↓ (parsing) ChangeTemplateFileContent ↓ (preview building) -TemplatePreviewChange +TemplatePreviewChange (unified) ↓ (loaded task building - template lookup from registry) -TemplateLoadedChange +AbstractTemplateLoadedChange + ├── SimpleTemplateLoadedChange (for AbstractSimpleTemplate) + └── SteppableTemplateLoadedChange (for AbstractSteppableTemplate) ↓ (execution preparation) -TemplateExecutableTask +TemplateExecutableTask + ├── SimpleTemplateExecutableTask (calls setStep()) + └── SteppableTemplateExecutableTask (calls setSteps()) ↓ (runtime execution) Template instance with injected dependencies ``` @@ -350,8 +354,9 @@ Template instance with injected dependencies **Key Classes in Flow**: - `ChangeTemplateFileContent` - YAML parsed data (`core/flamingock-core-commons`) - `TemplatePreviewTaskBuilder` - Builds preview from file content (`core/flamingock-core-commons`) -- `TemplateLoadedTaskBuilder` - Resolves template class, builds loaded change (`core/flamingock-core`) -- `TemplateExecutableTask` - Executes template with dependency injection (`core/flamingock-core`) +- `TemplateLoadedTaskBuilder` - Resolves template class, builds type-specific loaded change (`core/flamingock-core`) +- `TemplateExecutableTaskBuilder` - Builds type-specific executable task (`core/flamingock-core`) +- `TemplateExecutableTask` - Abstract base for template execution (`core/flamingock-core`) ### Discovery Mechanism (SPI) @@ -430,6 +435,33 @@ Templates are validated at compile-time to ensure YAML structure matches the tem - `io.flamingock.api.annotations.EnableFlamingock` - strictTemplateValidation flag - `io.flamingock.api.template.AbstractChangeTemplate` - template base classes +<<<<<<< Updated upstream +======= +### Template Class Hierarchy (Loaded & Executable) + +At the **Loaded** and **Executable** phases, templates are split into type-specific classes: + +**Loaded Phase:** +``` +AbstractTemplateLoadedChange (abstract base) +├── SimpleTemplateLoadedChange (apply, rollback) +└── SteppableTemplateLoadedChange (steps) +``` + +**Executable Phase:** +``` +TemplateExecutableTask (abstract base) +├── SimpleTemplateExecutableTask (calls setStep()) +└── SteppableTemplateExecutableTask (calls setSteps()) +``` + +**Type Detection:** Happens in `TemplateLoadedTaskBuilder.build()` using: +- `AbstractSteppableTemplate.class.isAssignableFrom(templateClass)` → SteppableTemplateLoadedChange +- Otherwise → SimpleTemplateLoadedChange (default for AbstractSimpleTemplate and unknown types) + +**Note:** Preview phase (`TemplatePreviewChange`) remains unified since YAML is parsed before template type is known. + +>>>>>>> Stashed changes ### Dependency Injection in Templates Template methods (`@Apply`, `@Rollback`) receive dependencies as **method parameters**, not constructor injection: diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java new file mode 100644 index 000000000..f1ce42cd5 --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.executable; + +import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.runtime.ExecutionRuntime; +import io.flamingock.internal.core.task.loaded.AbstractTemplateLoadedChange; +import io.flamingock.internal.util.FileUtil; +import io.flamingock.internal.util.log.FlamingockLoggerFactory; +import org.slf4j.Logger; + +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * Abstract base class for template executable tasks. + * Contains common logic for executing templates, with type-specific data setting + * delegated to subclasses. + * + * @param the type of template loaded change + */ +public abstract class AbstractTemplateExecutableTask extends ReflectionExecutableTask { + protected final Logger logger = FlamingockLoggerFactory.getLogger("TemplateTask"); + + public AbstractTemplateExecutableTask(String stageName, + T descriptor, + ChangeAction action, + Method executionMethod, + Method rollbackMethod) { + super(stageName, descriptor, action, executionMethod, rollbackMethod); + } + + protected void setConfigurationData(ExecutionRuntime executionRuntime, + ChangeTemplate instance) { + Class parameterClass = instance.getConfigurationClass(); + Object data = descriptor.getConfiguration(); + + if (data != null && Void.class != parameterClass) { + Method setConfigurationMethod = getSetterMethod(instance.getClass(), "setConfiguration"); + executionRuntime.executeMethodWithParameters( + instance, + setConfigurationMethod, + FileUtil.getFromMap(parameterClass, data)); + } else if (Void.class != parameterClass) { + logger.warn("No 'Configuration' section provided for template-based change[{}] of type[{}]", + descriptor.getId(), descriptor.getTemplateClass().getName()); + } + } + + protected Method getSetterMethod(Class changeTemplateClass, String methodName) { + return Arrays.stream(changeTemplateClass.getMethods()) + .filter(m -> methodName.equals(m.getName())) + .findFirst() + .orElseThrow(() -> new RuntimeException("Not found config setter for template: " + changeTemplateClass.getSimpleName())); + } + + +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java new file mode 100644 index 000000000..e468f1bfb --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.executable; + +import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.api.template.TemplateStep; +import io.flamingock.internal.common.core.error.ChangeExecutionException; +import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.runtime.ExecutionRuntime; +import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; +import io.flamingock.internal.util.FileUtil; + +import java.lang.reflect.Method; + +/** + * Executable task for simple templates (single apply/rollback step). + * Handles templates extending {@link AbstractSimpleTemplate}. + */ +public class SimpleTemplateExecutableTask extends AbstractTemplateExecutableTask { + + public SimpleTemplateExecutableTask(String stageName, + SimpleTemplateLoadedChange descriptor, + ChangeAction action, + Method executionMethod, + Method rollbackMethod) { + super(stageName, descriptor, action, executionMethod, rollbackMethod); + } + + @Override + protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { + try { + logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); + logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); + Object instance = executionRuntime.getInstance(descriptor.getConstructor()); + ChangeTemplate changeTemplateInstance = (ChangeTemplate) instance; + changeTemplateInstance.setTransactional(descriptor.isTransactional()); + changeTemplateInstance.setChangeId(descriptor.getId()); + setConfigurationData(executionRuntime, changeTemplateInstance); + + setTemplateData(executionRuntime, instance); + + executionRuntime.executeMethodWithInjectedDependencies(instance, method); + } catch (Throwable ex) { + throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void setTemplateData(ExecutionRuntime executionRuntime, Object instance) { + AbstractSimpleTemplate simpleTemplate = (AbstractSimpleTemplate) instance; + Object applyData = descriptor.getApply(); + + if (applyData != null) { + logger.debug("Setting step for simple template change[{}]", descriptor.getId()); + + TemplateStep step = convertToTemplateStep( + applyData, + descriptor.getRollback(), + simpleTemplate.getApplyPayloadClass(), + simpleTemplate.getRollbackPayloadClass() + ); + + Method setStepMethod = getSetterMethod(simpleTemplate.getClass(), "setStep"); + executionRuntime.executeMethodWithParameters(simpleTemplate, setStepMethod, step); + } else { + logger.warn("No 'apply' section provided for simple template-based change[{}]", descriptor.getId()); + } + } + + /** + * Converts raw apply/rollback data from YAML to a TemplateStep object. + * + * @param applyData the raw apply data + * @param rollbackData the raw rollback data (maybe null) + * @param applyClass the class type for apply payload + * @param rollbackClass the class type for rollback payload + * @return the converted TemplateStep object + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + protected TemplateStep convertToTemplateStep(Object applyData, + Object rollbackData, + Class applyClass, + Class rollbackClass) { + TemplateStep step = new TemplateStep(); + + if (applyData != null && Void.class != applyClass) { + step.setApply(FileUtil.getFromMap(applyClass, applyData)); + } + + if (rollbackData != null && Void.class != rollbackClass) { + step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); + } + + return step; + } + +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java new file mode 100644 index 000000000..ae959d600 --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -0,0 +1,130 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.executable; + +import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.api.template.TemplateStep; +import io.flamingock.internal.common.core.error.ChangeExecutionException; +import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.runtime.ExecutionRuntime; +import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; +import io.flamingock.internal.util.FileUtil; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Executable task for steppable templates (multiple steps). + * Handles templates extending {@link AbstractSteppableTemplate}. + */ +public class SteppableTemplateExecutableTask extends AbstractTemplateExecutableTask { + + public SteppableTemplateExecutableTask(String stageName, + SteppableTemplateLoadedChange descriptor, + ChangeAction action, + Method executionMethod, + Method rollbackMethod) { + super(stageName, descriptor, action, executionMethod, rollbackMethod); + } + + @Override + protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { + try { + logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); + logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); + Object instance = executionRuntime.getInstance(descriptor.getConstructor()); + ChangeTemplate changeTemplateInstance = (ChangeTemplate) instance; + changeTemplateInstance.setTransactional(descriptor.isTransactional()); + changeTemplateInstance.setChangeId(descriptor.getId()); + setConfigurationData(executionRuntime, changeTemplateInstance); + + setTemplateData(executionRuntime, instance); + + executionRuntime.executeMethodWithInjectedDependencies(instance, method); + } catch (Throwable ex) { + throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void setTemplateData(ExecutionRuntime executionRuntime, Object instance) { + AbstractSteppableTemplate steppableTemplate = (AbstractSteppableTemplate) instance; + Object stepsData = descriptor.getSteps(); + + if (stepsData != null) { + logger.debug("Setting steps for steppable template change[{}]", descriptor.getId()); + + List convertedSteps = convertToTemplateSteps( + stepsData, + steppableTemplate.getApplyPayloadClass(), + steppableTemplate.getRollbackPayloadClass() + ); + + Method setStepsMethod = getSetterMethod(steppableTemplate.getClass(), "setSteps"); + executionRuntime.executeMethodWithParameters(steppableTemplate, setStepsMethod, convertedSteps); + } else { + logger.warn("No 'steps' section provided for steppable template-based change[{}]", descriptor.getId()); + } + } + + + /** + * Converts raw step data (List of Maps from YAML) to a list of TemplateStep objects. + * + * @param stepsData the raw steps data (expected to be a List of Maps) + * @param applyClass the class type for apply payloads + * @param rollbackClass the class type for rollback payloads + * @return list of converted TemplateStep objects + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + protected List convertToTemplateSteps(Object stepsData, + Class applyClass, + Class rollbackClass) { + List result = new ArrayList<>(); + + if (!(stepsData instanceof List)) { + logger.warn("Steps data is not a List, ignoring"); + return result; + } + + List stepsList = (List) stepsData; + for (Object stepItem : stepsList) { + if (stepItem instanceof Map) { + Map stepMap = (Map) stepItem; + TemplateStep step = new TemplateStep(); + + Object applyData = stepMap.get("apply"); + if (applyData != null && Void.class != applyClass) { + step.setApply(FileUtil.getFromMap(applyClass, applyData)); + } + + Object rollbackData = stepMap.get("rollback"); + if (rollbackData != null && Void.class != rollbackClass) { + step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); + } + + result.add(step); + } else if (stepItem instanceof TemplateStep) { + result.add((TemplateStep) stepItem); + } + } + + return result; + } +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/TemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/TemplateExecutableTask.java deleted file mode 100644 index 176b0e5f0..000000000 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/TemplateExecutableTask.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2023 Flamingock (https://www.flamingock.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.flamingock.internal.core.task.executable; - -import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.template.ChangeTemplate; -import io.flamingock.api.template.TemplateStep; -import io.flamingock.internal.common.core.error.ChangeExecutionException; -import io.flamingock.internal.core.runtime.ExecutionRuntime; -import io.flamingock.internal.core.task.loaded.TemplateLoadedChange; -import io.flamingock.internal.common.core.recovery.action.ChangeAction; -import io.flamingock.internal.util.FileUtil; -import io.flamingock.internal.util.log.FlamingockLoggerFactory; -import org.slf4j.Logger; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -public class TemplateExecutableTask extends ReflectionExecutableTask { - private final Logger logger = FlamingockLoggerFactory.getLogger("TemplateTask"); - - public TemplateExecutableTask(String stageName, - TemplateLoadedChange descriptor, - ChangeAction action, - Method executionMethod, - Method rollbackMethod) { - super(stageName, descriptor, action, executionMethod, rollbackMethod); - } - - @Override - protected void executeInternal(ExecutionRuntime executionRuntime, Method method ) { - try { - logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); - logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - ChangeTemplate changeTemplateInstance = (ChangeTemplate) instance; - changeTemplateInstance.setTransactional(descriptor.isTransactional()); - changeTemplateInstance.setChangeId(descriptor.getId()); - setConfigurationData(executionRuntime, changeTemplateInstance); - - if (instance instanceof AbstractSteppableTemplate) { - setStepsForSteppableTemplate(executionRuntime, (AbstractSteppableTemplate) instance); - } else if (instance instanceof AbstractSimpleTemplate) { - setStepForSimpleTemplate(executionRuntime, (AbstractSimpleTemplate) instance); - } - - executionRuntime.executeMethodWithInjectedDependencies(instance, method); - } catch (Throwable ex) { - throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); - } - } - - /** - * Sets the steps for an AbstractSteppableTemplate. - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private void setStepsForSteppableTemplate(ExecutionRuntime executionRuntime, - AbstractSteppableTemplate instance) { - Object stepsData = descriptor.getSteps(); - - if (stepsData != null) { - logger.debug("Setting steps for steppable template change[{}]", descriptor.getId()); - - List convertedSteps = convertToTemplateSteps( - stepsData, - instance.getApplyPayloadClass(), - instance.getRollbackPayloadClass() - ); - - Method setStepsMethod = getSetterMethod(instance.getClass(), "setSteps"); - executionRuntime.executeMethodWithParameters(instance, setStepsMethod, convertedSteps); - } else { - logger.warn("No 'steps' section provided for steppable template-based change[{}]", descriptor.getId()); - } - } - - /** - * Sets the step for an AbstractSimpleTemplate. - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private void setStepForSimpleTemplate(ExecutionRuntime executionRuntime, - AbstractSimpleTemplate instance) { - Object applyData = descriptor.getApply(); - - if (applyData != null) { - logger.debug("Setting step for simple template change[{}]", descriptor.getId()); - - TemplateStep step = convertToTemplateStep( - applyData, - descriptor.getRollback(), - instance.getApplyPayloadClass(), - instance.getRollbackPayloadClass() - ); - - Method setStepMethod = getSetterMethod(instance.getClass(), "setStep"); - executionRuntime.executeMethodWithParameters(instance, setStepMethod, step); - } else { - logger.warn("No 'apply' section provided for simple template-based change[{}]", descriptor.getId()); - } - } - - private void setConfigurationData(ExecutionRuntime executionRuntime, - ChangeTemplate instance) { - Class parameterClass = instance.getConfigurationClass(); - Object data = descriptor.getConfiguration(); - - if (data != null && Void.class != parameterClass) { - Method setConfigurationMethod = getSetterMethod(instance.getClass(), "setConfiguration"); - executionRuntime.executeMethodWithParameters( - instance, - setConfigurationMethod, - FileUtil.getFromMap(parameterClass, data)); - } else if (Void.class != parameterClass) { - logger.warn("No 'Configuration' section provided for template-based change[{}] of type[{}]", - descriptor.getId(), descriptor.getTemplateClass().getName()); - } - } - - - private Method getSetterMethod(Class changeTemplateClass, String methodName) { - - return Arrays.stream(changeTemplateClass.getMethods()) - .filter(m-> methodName.equals(m.getName())) - .findFirst() - .orElseThrow(()-> new RuntimeException("Not found config setter for template: " + changeTemplateClass.getSimpleName())); - - } - - /** - * Converts raw apply/rollback data from YAML to a TemplateStep object. - * - * @param applyData the raw apply data - * @param rollbackData the raw rollback data (maybe null) - * @param applyClass the class type for apply payload - * @param rollbackClass the class type for rollback payload - * @return the converted TemplateStep object - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private TemplateStep convertToTemplateStep(Object applyData, - Object rollbackData, - Class applyClass, - Class rollbackClass) { - TemplateStep step = new TemplateStep(); - - if (applyData != null && Void.class != applyClass) { - step.setApply(FileUtil.getFromMap(applyClass, applyData)); - } - - if (rollbackData != null && Void.class != rollbackClass) { - step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); - } - - return step; - } - - /** - * Converts raw step data (List of Maps from YAML) to a list of TemplateStep objects. - * - * @param stepsData the raw steps data (expected to be a List of Maps) - * @param applyClass the class type for apply payloads - * @param rollbackClass the class type for rollback payloads - * @return list of converted TemplateStep objects - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private List convertToTemplateSteps(Object stepsData, - Class applyClass, - Class rollbackClass) { - List result = new ArrayList<>(); - - if (!(stepsData instanceof List)) { - logger.warn("Steps data is not a List, ignoring"); - return result; - } - - List stepsList = (List) stepsData; - for (Object stepItem : stepsList) { - if (stepItem instanceof Map) { - Map stepMap = (Map) stepItem; - TemplateStep step = new TemplateStep(); - - Object applyData = stepMap.get("apply"); - if (applyData != null && Void.class != applyClass) { - step.setApply(FileUtil.getFromMap(applyClass, applyData)); - } - - Object rollbackData = stepMap.get("rollback"); - if (rollbackData != null && Void.class != rollbackClass) { - step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); - } - - result.add(step); - } else if (stepItem instanceof TemplateStep) { - result.add((TemplateStep) stepItem); - } - } - - return result; - } - - - - -} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java index 5b566beeb..d8669ed49 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java @@ -18,8 +18,8 @@ import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.task.executable.ExecutableTask; import io.flamingock.internal.core.task.loaded.AbstractLoadedTask; +import io.flamingock.internal.core.task.loaded.AbstractTemplateLoadedChange; import io.flamingock.internal.core.task.loaded.CodeLoadedChange; -import io.flamingock.internal.core.task.loaded.TemplateLoadedChange; public interface ExecutableTaskBuilder { @@ -43,7 +43,7 @@ static ExecutableTaskBuilder getInstance(AbstractLoadedTask loadedTask) { if(TemplateExecutableTaskBuilder.supports(loadedTask)) { TemplateExecutableTaskBuilder templateBuilder = TemplateExecutableTaskBuilder.getInstance(); - TemplateLoadedChange castedTask = templateBuilder.cast(loadedTask); + AbstractTemplateLoadedChange castedTask = templateBuilder.cast(loadedTask); return templateBuilder.setLoadedTask(castedTask); } else if(CodeExecutableTaskBuilder.supports(loadedTask)) { diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java index ad14e6467..8459ba450 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java @@ -15,12 +15,14 @@ */ package io.flamingock.internal.core.task.executable.builder; -import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.task.executable.ExecutableTask; -import io.flamingock.internal.core.task.executable.TemplateExecutableTask; +import io.flamingock.internal.core.task.executable.SimpleTemplateExecutableTask; +import io.flamingock.internal.core.task.executable.SteppableTemplateExecutableTask; import io.flamingock.internal.core.task.loaded.AbstractLoadedTask; -import io.flamingock.internal.core.task.loaded.TemplateLoadedChange; +import io.flamingock.internal.core.task.loaded.AbstractTemplateLoadedChange; +import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; +import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; import io.flamingock.internal.util.log.FlamingockLoggerFactory; import org.slf4j.Logger; @@ -30,29 +32,29 @@ /** * Factory for Change classes */ -public class TemplateExecutableTaskBuilder implements ExecutableTaskBuilder { +public class TemplateExecutableTaskBuilder implements ExecutableTaskBuilder { private final static Logger logger = FlamingockLoggerFactory.getLogger("TemplateBuilder"); private static final TemplateExecutableTaskBuilder instance = new TemplateExecutableTaskBuilder(); private String stageName; private ChangeAction changeAction; - private TemplateLoadedChange loadedTask; + private AbstractTemplateLoadedChange loadedTask; static TemplateExecutableTaskBuilder getInstance() { return instance; } public static boolean supports(AbstractLoadedTask loadedTask) { - return TemplateLoadedChange.class.isAssignableFrom(loadedTask.getClass()); + return AbstractTemplateLoadedChange.class.isAssignableFrom(loadedTask.getClass()); } @Override - public TemplateLoadedChange cast(AbstractLoadedTask loadedTask) { - return (TemplateLoadedChange) loadedTask; + public AbstractTemplateLoadedChange cast(AbstractLoadedTask loadedTask) { + return (AbstractTemplateLoadedChange) loadedTask; } @Override - public TemplateExecutableTaskBuilder setLoadedTask(TemplateLoadedChange loadedTask) { + public TemplateExecutableTaskBuilder setLoadedTask(AbstractTemplateLoadedChange loadedTask) { this.loadedTask = loadedTask; return this; } @@ -72,57 +74,50 @@ public TemplateExecutableTaskBuilder setChangeAction(ChangeAction action) { @Override public ExecutableTask build() { - return getTasksFromReflection(stageName, loadedTask, changeAction); - } - - - /** - * New ChangeAction-based method for building tasks. - */ - private TemplateExecutableTask getTasksFromReflection(String stageName, - TemplateLoadedChange loadedTask, - ChangeAction action) { - return buildTask(stageName, loadedTask, action); - } - - private TemplateExecutableTask buildTask(String stageName, - TemplateLoadedChange loadedTask, - ChangeAction action) { - Method rollbackMethod = null; - - boolean isSteppableTemplate = AbstractSteppableTemplate.class.isAssignableFrom(loadedTask.getTemplateClass()); - - if (isSteppableTemplate) { - rollbackMethod = loadedTask.getRollbackMethod().orElse(null); - if (rollbackMethod != null) { - logger.trace("Change[{}] is a steppable template with rollback method", loadedTask.getId()); - } - } else if (loadedTask.getRollback() != null) { - rollbackMethod = loadedTask.getRollbackMethod().orElse(null); - if (rollbackMethod != null) { - logger.trace("Change[{}] provides rollback in configuration", loadedTask.getId()); + Method rollbackMethod = loadedTask.getRollbackMethod().orElse(null); + + if (loadedTask instanceof SimpleTemplateLoadedChange) { + SimpleTemplateLoadedChange simple = (SimpleTemplateLoadedChange) loadedTask; + // Only include rollback method if rollback data is present + if (simple.getRollback() != null) { + if (rollbackMethod != null) { + logger.trace("Change[{}] provides rollback in configuration", loadedTask.getId()); + } else { + logger.warn("Change[{}] provides rollback in configuration, but based on a template[{}] not supporting manual rollback", + loadedTask.getId(), + loadedTask.getSource() + ); + } } else { - logger.warn("Change[{}] provides rollback in configuration, but based on a template[{}] not supporting manual rollback", - loadedTask.getId(), - loadedTask.getSource() - ); + if (rollbackMethod != null) { + logger.warn("Change[{}] does not provide rollback, but based on a template[{}] support manual rollback", + loadedTask.getId(), + loadedTask.getSource() + ); + } + rollbackMethod = null; } - } else { - if (loadedTask.getRollbackMethod().isPresent()) { - logger.warn("Change[{}] does not provide rollback, but based on a template[{}] support manual rollback", - loadedTask.getId(), - loadedTask.getSource() - ); + return new SimpleTemplateExecutableTask( + stageName, + simple, + changeAction, + loadedTask.getApplyMethod(), + rollbackMethod + ); + } else if (loadedTask instanceof SteppableTemplateLoadedChange) { + SteppableTemplateLoadedChange steppable = (SteppableTemplateLoadedChange) loadedTask; + if (rollbackMethod != null) { + logger.trace("Change[{}] is a steppable template with rollback method", loadedTask.getId()); } + return new SteppableTemplateExecutableTask( + stageName, + steppable, + changeAction, + loadedTask.getApplyMethod(), + rollbackMethod + ); } - return new TemplateExecutableTask( - stageName, - loadedTask, - action, - loadedTask.getApplyMethod(), - rollbackMethod - ); - + throw new IllegalArgumentException("Unknown template type: " + loadedTask.getClass().getName()); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java similarity index 65% rename from core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedChange.java rename to core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java index c30e84a07..fa713d33f 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java @@ -18,71 +18,52 @@ import io.flamingock.api.annotations.Apply; import io.flamingock.api.annotations.Rollback; import io.flamingock.api.template.ChangeTemplate; -import io.flamingock.internal.common.core.preview.PreviewConstructor; -import io.flamingock.internal.util.ReflectionUtil; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; +import io.flamingock.internal.util.ReflectionUtil; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.List; import java.util.Optional; - -public class TemplateLoadedChange extends AbstractLoadedChange { +/** + * Abstract base class for template-based loaded changes. + * Contains common fields and methods shared by both SimpleTemplateLoadedChange + * and SteppableTemplateLoadedChange. + */ +public abstract class AbstractTemplateLoadedChange extends AbstractLoadedChange { private final List profiles; private final Object configuration; - private final Object apply; - private final Object rollback; - private final Object steps; - TemplateLoadedChange(String changeFileName, - String id, - String order, - String author, - Class> templateClass, - Constructor constructor, - List profiles, - boolean transactional, - boolean runAlways, - boolean systemTask, - Object configuration, - Object apply, - Object rollback, - Object steps, - TargetSystemDescriptor targetSystem, - RecoveryDescriptor recovery) { + protected AbstractTemplateLoadedChange(String changeFileName, + String id, + String order, + String author, + Class> templateClass, + Constructor constructor, + List profiles, + boolean transactional, + boolean runAlways, + boolean systemTask, + Object configuration, + TargetSystemDescriptor targetSystem, + RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, runAlways, transactional, systemTask, targetSystem, recovery, false); this.profiles = profiles; this.transactional = transactional; this.configuration = configuration; - this.apply = apply; - this.rollback = rollback; - this.steps = steps; } public Object getConfiguration() { return configuration; } - public Object getApply() { - return apply; - } - - public Object getRollback() { - return rollback; - } - - public Object getSteps() { - return steps; - } - public List getProfiles() { return profiles; } - @SuppressWarnings("unchecked") public Class> getTemplateClass() { return (Class>) this.getImplementationClass(); @@ -101,5 +82,4 @@ public Method getApplyMethod() { public Optional getRollbackMethod() { return ReflectionUtil.findFirstAnnotatedMethod(getImplementationClass(), Rollback.class); } - } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java new file mode 100644 index 000000000..73201184a --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.loaded; + +import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.internal.common.core.task.RecoveryDescriptor; +import io.flamingock.internal.common.core.task.TargetSystemDescriptor; + +import java.lang.reflect.Constructor; +import java.util.List; + +/** + * Loaded change for simple templates (single apply/rollback step). + * Used for templates extending {@link io.flamingock.api.template.AbstractSimpleTemplate}. + */ +public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { + + private final Object apply; + private final Object rollback; + + SimpleTemplateLoadedChange(String changeFileName, + String id, + String order, + String author, + Class> templateClass, + Constructor constructor, + List profiles, + boolean transactional, + boolean runAlways, + boolean systemTask, + Object configuration, + Object apply, + Object rollback, + TargetSystemDescriptor targetSystem, + RecoveryDescriptor recovery) { + super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); + this.apply = apply; + this.rollback = rollback; + } + + public Object getApply() { + return apply; + } + + public Object getRollback() { + return rollback; + } +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java new file mode 100644 index 000000000..bf1a48d08 --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.loaded; + +import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.internal.common.core.task.RecoveryDescriptor; +import io.flamingock.internal.common.core.task.TargetSystemDescriptor; + +import java.lang.reflect.Constructor; +import java.util.List; + +/** + * Loaded change for steppable templates (multiple steps). + * Used for templates extending {@link io.flamingock.api.template.AbstractSteppableTemplate}. + */ +public class SteppableTemplateLoadedChange extends AbstractTemplateLoadedChange { + + private final Object steps; + + SteppableTemplateLoadedChange(String changeFileName, + String id, + String order, + String author, + Class> templateClass, + Constructor constructor, + List profiles, + boolean transactional, + boolean runAlways, + boolean systemTask, + Object configuration, + Object steps, + TargetSystemDescriptor targetSystem, + RecoveryDescriptor recovery) { + super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); + this.steps = steps; + } + + public Object getSteps() { + return steps; + } +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index dd0776896..ab661df5f 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -15,6 +15,8 @@ */ package io.flamingock.internal.core.task.loaded; +import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.api.template.ChangeTemplate; import io.flamingock.internal.common.core.preview.PreviewConstructor; @@ -31,7 +33,7 @@ //TODO how to set transactional and runAlways -public class TemplateLoadedTaskBuilder implements LoadedTaskBuilder { +public class TemplateLoadedTaskBuilder implements LoadedTaskBuilder { private String fileName; private String id; @@ -141,30 +143,49 @@ public TemplateLoadedTaskBuilder setSteps(Object steps) { } @Override - public TemplateLoadedChange build() { + public AbstractTemplateLoadedChange build() { // boolean isTaskTransactional = true;//TODO implement this. isTaskTransactionalAccordingTemplate(templateSpec); Class> templateClass = ChangeTemplateManager.getTemplate(templateName) .orElseThrow(()-> new FlamingockException(String.format("Template[%s] not found. This is probably because template's name is wrong or template's library not imported", templateName))); Constructor constructor = ReflectionUtil.getDefaultConstructor(templateClass); - return new TemplateLoadedChange( - fileName, - id, - order, - author, - templateClass, - constructor, - profiles, - transactional, - runAlways, - system, - configuration, - apply, - rollback, - steps, - targetSystem, - recovery); + // Determine template type and build appropriate loaded change + if (AbstractSteppableTemplate.class.isAssignableFrom(templateClass)) { + return new SteppableTemplateLoadedChange( + fileName, + id, + order, + author, + templateClass, + constructor, + profiles, + transactional, + runAlways, + system, + configuration, + steps, + targetSystem, + recovery); + } else { + // Default to SimpleTemplateLoadedChange for AbstractSimpleTemplate and unknown types + return new SimpleTemplateLoadedChange( + fileName, + id, + order, + author, + templateClass, + constructor, + profiles, + transactional, + runAlways, + system, + configuration, + apply, + rollback, + targetSystem, + recovery); + } } diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java index 7d4bc20d3..e09cc39e5 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java @@ -73,9 +73,10 @@ void shouldBuildWithOrderInContentWhenOrderInContentPresentAndNoOrderInFileName( builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SimpleTemplateLoadedChange.class, result); assertEquals("001", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); @@ -103,9 +104,10 @@ void shouldBuildWithOrderFromFileNameWhenOrderInContentIsNullAndOrderInFileNameI builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SimpleTemplateLoadedChange.class, result); assertEquals("0002", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("_0002__test-file.yml", result.getFileName()); @@ -133,9 +135,10 @@ void shouldBuildWithOrderInContentWhenOrderInContentMatchesOrderInFileName() { .setRollback(new Object()); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SimpleTemplateLoadedChange.class, result); assertEquals("003", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("_003__test-file.yml", result.getFileName()); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index 24262e1ff..14b8fb815 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -77,15 +77,17 @@ void shouldBuildWithStepsWhenStepsProvided() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); - assertNotNull(result.getSteps()); + assertNotNull(steppableResult.getSteps()); // Steps are stored as raw Object - conversion happens at execution time - assertEquals(rawSteps, result.getSteps()); + assertEquals(rawSteps, steppableResult.getSteps()); } } @@ -113,9 +115,10 @@ void shouldBuildWithOrderInContentForSteppableTemplate() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); assertEquals("001", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); @@ -146,9 +149,10 @@ void shouldBuildWithOrderFromFileNameForSteppableTemplate() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); assertEquals("0002", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("_0002__test-file.yml", result.getFileName()); @@ -173,13 +177,15 @@ void shouldBuildWithEmptyStepsList() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); - assertNotNull(result.getSteps()); - assertTrue(((List) result.getSteps()).isEmpty()); + assertNotNull(steppableResult.getSteps()); + assertTrue(((List) steppableResult.getSteps()).isEmpty()); } } @@ -206,14 +212,16 @@ void shouldBuildWithMultipleSteps() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("multi-step-change", result.getId()); - assertNotNull(result.getSteps()); + assertNotNull(steppableResult.getSteps()); @SuppressWarnings("unchecked") - List> resultSteps = (List>) result.getSteps(); + List> resultSteps = (List>) steppableResult.getSteps(); assertEquals(3, resultSteps.size()); // Verify steps are preserved in order assertEquals("createCollection", resultSteps.get(0).get("apply")); @@ -268,12 +276,14 @@ void shouldBuildWithStepsHavingOnlyApply() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); @SuppressWarnings("unchecked") - List> resultSteps = (List>) result.getSteps(); + List> resultSteps = (List>) steppableResult.getSteps(); assertEquals(2, resultSteps.size()); assertNull(resultSteps.get(0).get("rollback")); assertNull(resultSteps.get(1).get("rollback")); @@ -296,12 +306,14 @@ void shouldBuildWithNullSteps() { builder.setProfiles(Arrays.asList("test")); // When - TemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then + assertInstanceOf(SteppableTemplateLoadedChange.class, result); + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); - assertNull(result.getSteps()); + assertNull(steppableResult.getSteps()); } } diff --git a/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java b/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java index 47a128782..888f0de05 100644 --- a/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java +++ b/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java @@ -32,8 +32,10 @@ import io.flamingock.internal.core.task.loaded.AbstractLoadedChange; import io.flamingock.internal.core.task.loaded.AbstractLoadedTask; import io.flamingock.internal.core.task.loaded.AbstractReflectionLoadedTask; +import io.flamingock.internal.core.task.loaded.AbstractTemplateLoadedChange; import io.flamingock.internal.core.task.loaded.CodeLoadedChange; -import io.flamingock.internal.core.task.loaded.TemplateLoadedChange; +import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; +import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; import io.flamingock.internal.util.log.FlamingockLoggerFactory; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; @@ -73,7 +75,9 @@ private static void registerInternalClasses() { registerClassForReflection(AbstractReflectionLoadedTask.class.getName()); registerClassForReflection(AbstractLoadedChange.class.getName()); registerClassForReflection(CodeLoadedChange.class.getName()); - registerClassForReflection(TemplateLoadedChange.class.getName()); + registerClassForReflection(AbstractTemplateLoadedChange.class); + registerClassForReflection(SimpleTemplateLoadedChange.class); + registerClassForReflection(SteppableTemplateLoadedChange.class); //others registerClassForReflection(CoderResult.class.getName()); @@ -86,7 +90,9 @@ private static void initializeInternalClassesAtBuildTime() { logger.startInitializationProcess("internal classes"); initializeClassAtBuildTime(CodeLoadedChange.class); initializeClassAtBuildTime(AbstractLoadedChange.class); - initializeClassAtBuildTime(TemplateLoadedChange.class); + initializeClassAtBuildTime(AbstractTemplateLoadedChange.class); + initializeClassAtBuildTime(SimpleTemplateLoadedChange.class); + initializeClassAtBuildTime(SteppableTemplateLoadedChange.class); initializeClassAtBuildTime(ChangeTemplateManager.class); initializeClassAtBuildTime(RecoveryDescriptor.class); initializeClassAtBuildTime(FlamingockLoggerFactory.class); diff --git a/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/SpringbootProfileFilter.java b/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/SpringbootProfileFilter.java index 3fd787c8a..38394680f 100644 --- a/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/SpringbootProfileFilter.java +++ b/platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/SpringbootProfileFilter.java @@ -18,8 +18,8 @@ import io.flamingock.internal.core.task.filter.TaskFilter; import io.flamingock.internal.core.task.loaded.AbstractLoadedTask; import io.flamingock.internal.core.task.loaded.AbstractReflectionLoadedTask; +import io.flamingock.internal.core.task.loaded.AbstractTemplateLoadedChange; import io.flamingock.internal.core.task.loaded.CodeLoadedChange; -import io.flamingock.internal.core.task.loaded.TemplateLoadedChange; import org.springframework.context.annotation.Profile; import java.util.Arrays; @@ -49,8 +49,8 @@ public boolean filter(AbstractLoadedTask descriptor) { } private boolean filter(AbstractReflectionLoadedTask reflectionDescriptor) { - if (TemplateLoadedChange.class.isAssignableFrom(reflectionDescriptor.getClass())) { - return filterTemplateChange((TemplateLoadedChange) reflectionDescriptor); + if (AbstractTemplateLoadedChange.class.isAssignableFrom(reflectionDescriptor.getClass())) { + return filterTemplateChange((AbstractTemplateLoadedChange) reflectionDescriptor); } else if (CodeLoadedChange.class.isAssignableFrom(reflectionDescriptor.getClass())) { return filterCodeChange((CodeLoadedChange) reflectionDescriptor); @@ -65,7 +65,7 @@ private boolean filter(AbstractReflectionLoadedTask reflectionDescriptor) { } - private boolean filterTemplateChange(TemplateLoadedChange reflectionDescriptor) { + private boolean filterTemplateChange(AbstractTemplateLoadedChange reflectionDescriptor) { return filterProfiles(reflectionDescriptor.getProfiles()); } From 743479ca1e6f311cb10e08118e555fc8b900c368 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 09:00:19 +0000 Subject: [PATCH 02/19] refactor: wip(2) --- .../AbstractTemplateExecutableTask.java | 16 ++++++++-------- .../executable/SimpleTemplateExecutableTask.java | 15 ++++++++++----- .../SteppableTemplateExecutableTask.java | 9 ++++----- .../loaded/AbstractTemplateLoadedChange.java | 2 +- .../task/loaded/TemplateLoadedTaskBuilder.java | 2 ++ 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java index f1ce42cd5..894c3c420 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java @@ -44,17 +44,17 @@ public AbstractTemplateExecutableTask(String stageName, super(stageName, descriptor, action, executionMethod, rollbackMethod); } - protected void setConfigurationData(ExecutionRuntime executionRuntime, - ChangeTemplate instance) { - Class parameterClass = instance.getConfigurationClass(); + protected ChangeTemplate getInstance(ExecutionRuntime executionRuntime) { + Object instance = executionRuntime.getInstance(descriptor.getConstructor()); + return (ChangeTemplate) instance; + } + + protected void setConfigurationData(ChangeTemplate instance) { + Class parameterClass = instance.getConfigurationClass(); Object data = descriptor.getConfiguration(); if (data != null && Void.class != parameterClass) { - Method setConfigurationMethod = getSetterMethod(instance.getClass(), "setConfiguration"); - executionRuntime.executeMethodWithParameters( - instance, - setConfigurationMethod, - FileUtil.getFromMap(parameterClass, data)); + instance.setConfiguration(FileUtil.getFromMap(parameterClass, data)); } else if (Void.class != parameterClass) { logger.warn("No 'Configuration' section provided for template-based change[{}] of type[{}]", descriptor.getId(), descriptor.getTemplateClass().getName()); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index e468f1bfb..637969e02 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -23,6 +23,7 @@ import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; import io.flamingock.internal.util.FileUtil; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Method; @@ -45,20 +46,24 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - ChangeTemplate changeTemplateInstance = (ChangeTemplate) instance; + ChangeTemplate changeTemplateInstance = getInstance(executionRuntime); changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); - setConfigurationData(executionRuntime, changeTemplateInstance); + setConfigurationData(changeTemplateInstance); - setTemplateData(executionRuntime, instance); + setTemplateData(executionRuntime, changeTemplateInstance); - executionRuntime.executeMethodWithInjectedDependencies(instance, method); + executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); } catch (Throwable ex) { throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); } } + protected AbstractSimpleTemplate getInstance(ExecutionRuntime executionRuntime) { + Object instance = executionRuntime.getInstance(descriptor.getConstructor()); + return (AbstractSimpleTemplate) instance; + } + @SuppressWarnings({"unchecked", "rawtypes"}) protected void setTemplateData(ExecutionRuntime executionRuntime, Object instance) { AbstractSimpleTemplate simpleTemplate = (AbstractSimpleTemplate) instance; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index ae959d600..07a494ebe 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -48,15 +48,14 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - ChangeTemplate changeTemplateInstance = (ChangeTemplate) instance; + ChangeTemplate changeTemplateInstance = getInstance(executionRuntime); changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); - setConfigurationData(executionRuntime, changeTemplateInstance); + setConfigurationData(changeTemplateInstance); - setTemplateData(executionRuntime, instance); + setTemplateData(executionRuntime, changeTemplateInstance); - executionRuntime.executeMethodWithInjectedDependencies(instance, method); + executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); } catch (Throwable ex) { throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java index fa713d33f..2d88e9fb8 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java @@ -41,7 +41,7 @@ protected AbstractTemplateLoadedChange(String changeFileName, String id, String order, String author, - Class> templateClass, + Class templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index ab661df5f..bf6ceea48 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -169,6 +169,8 @@ public AbstractTemplateLoadedChange build() { recovery); } else { // Default to SimpleTemplateLoadedChange for AbstractSimpleTemplate and unknown types + Class steppableTemplateClass = + templateClass.asSubclass(AbstractSimpleTemplate.class); return new SimpleTemplateLoadedChange( fileName, id, From 6768a6c479392e61fa6ed10a9a05fefdda17bf70 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 09:16:58 +0000 Subject: [PATCH 03/19] refactor: wip(3) --- .../task/loaded/AbstractTemplateLoadedChange.java | 2 +- .../task/loaded/SimpleTemplateLoadedChange.java | 3 ++- .../task/loaded/SteppableTemplateLoadedChange.java | 3 ++- .../task/loaded/TemplateLoadedTaskBuilder.java | 14 ++++++++++---- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java index 2d88e9fb8..fa713d33f 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java @@ -41,7 +41,7 @@ protected AbstractTemplateLoadedChange(String changeFileName, String id, String order, String author, - Class templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java index 73201184a..7ef23adeb 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.loaded; +import io.flamingock.api.template.AbstractSimpleTemplate; import io.flamingock.api.template.ChangeTemplate; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -35,7 +36,7 @@ public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java index bf1a48d08..4133fbf9c 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.loaded; +import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.ChangeTemplate; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -34,7 +35,7 @@ public class SteppableTemplateLoadedChange extends AbstractTemplateLoadedChange String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index bf6ceea48..a7fbfa44a 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -152,12 +152,16 @@ public AbstractTemplateLoadedChange build() { // Determine template type and build appropriate loaded change if (AbstractSteppableTemplate.class.isAssignableFrom(templateClass)) { + + @SuppressWarnings("unchecked") + Class> steppableTemplateClass = (Class>) + templateClass.asSubclass(AbstractSteppableTemplate.class); return new SteppableTemplateLoadedChange( fileName, id, order, author, - templateClass, + steppableTemplateClass, constructor, profiles, transactional, @@ -169,14 +173,16 @@ public AbstractTemplateLoadedChange build() { recovery); } else { // Default to SimpleTemplateLoadedChange for AbstractSimpleTemplate and unknown types - Class steppableTemplateClass = - templateClass.asSubclass(AbstractSimpleTemplate.class); + @SuppressWarnings("unchecked") + Class> simpleTemplateClass = (Class>) + templateClass.asSubclass(AbstractSimpleTemplate.class); + return new SimpleTemplateLoadedChange( fileName, id, order, author, - templateClass, + simpleTemplateClass, constructor, profiles, transactional, From 18a7798c339bc19a07414898a4cd222f27741239 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 09:53:11 +0000 Subject: [PATCH 04/19] refactor: wip(3) --- .../AbstractTemplateExecutableTask.java | 5 --- .../SimpleTemplateExecutableTask.java | 39 +++++-------------- .../SteppableTemplateExecutableTask.java | 18 +++++---- 3 files changed, 21 insertions(+), 41 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java index 894c3c420..65cca3039 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java @@ -44,11 +44,6 @@ public AbstractTemplateExecutableTask(String stageName, super(stageName, descriptor, action, executionMethod, rollbackMethod); } - protected ChangeTemplate getInstance(ExecutionRuntime executionRuntime) { - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - return (ChangeTemplate) instance; - } - protected void setConfigurationData(ChangeTemplate instance) { Class parameterClass = instance.getConfigurationClass(); Object data = descriptor.getConfiguration(); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 637969e02..6536e4ced 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -16,14 +16,12 @@ package io.flamingock.internal.core.task.executable; import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.ChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; import io.flamingock.internal.util.FileUtil; -import org.jetbrains.annotations.NotNull; import java.lang.reflect.Method; @@ -46,12 +44,14 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - ChangeTemplate changeTemplateInstance = getInstance(executionRuntime); + AbstractSimpleTemplate changeTemplateInstance = (AbstractSimpleTemplate) + executionRuntime.getInstance(descriptor.getConstructor()); + changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); setConfigurationData(changeTemplateInstance); - setTemplateData(executionRuntime, changeTemplateInstance); + setTemplateData(changeTemplateInstance); executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); } catch (Throwable ex) { @@ -59,14 +59,8 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) } } - protected AbstractSimpleTemplate getInstance(ExecutionRuntime executionRuntime) { - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - return (AbstractSimpleTemplate) instance; - } - @SuppressWarnings({"unchecked", "rawtypes"}) - protected void setTemplateData(ExecutionRuntime executionRuntime, Object instance) { - AbstractSimpleTemplate simpleTemplate = (AbstractSimpleTemplate) instance; + protected void setTemplateData(AbstractSimpleTemplate instance) { Object applyData = descriptor.getApply(); if (applyData != null) { @@ -75,37 +69,24 @@ protected void setTemplateData(ExecutionRuntime executionRuntime, Object instanc TemplateStep step = convertToTemplateStep( applyData, descriptor.getRollback(), - simpleTemplate.getApplyPayloadClass(), - simpleTemplate.getRollbackPayloadClass() + instance.getApplyPayloadClass(), + instance.getRollbackPayloadClass() ); + instance.setStep(step); - Method setStepMethod = getSetterMethod(simpleTemplate.getClass(), "setStep"); - executionRuntime.executeMethodWithParameters(simpleTemplate, setStepMethod, step); } else { logger.warn("No 'apply' section provided for simple template-based change[{}]", descriptor.getId()); } } - /** - * Converts raw apply/rollback data from YAML to a TemplateStep object. - * - * @param applyData the raw apply data - * @param rollbackData the raw rollback data (maybe null) - * @param applyClass the class type for apply payload - * @param rollbackClass the class type for rollback payload - * @return the converted TemplateStep object - */ + @SuppressWarnings({"unchecked", "rawtypes"}) protected TemplateStep convertToTemplateStep(Object applyData, Object rollbackData, Class applyClass, Class rollbackClass) { TemplateStep step = new TemplateStep(); - - if (applyData != null && Void.class != applyClass) { - step.setApply(FileUtil.getFromMap(applyClass, applyData)); - } - + step.setApply(FileUtil.getFromMap(applyClass, applyData)); if (rollbackData != null && Void.class != rollbackClass) { step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index 07a494ebe..ba7748b8a 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.executable; +import io.flamingock.api.template.AbstractSimpleTemplate; import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.ChangeTemplate; import io.flamingock.api.template.TemplateStep; @@ -48,7 +49,10 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - ChangeTemplate changeTemplateInstance = getInstance(executionRuntime); + + AbstractSteppableTemplate changeTemplateInstance = (AbstractSteppableTemplate) + executionRuntime.getInstance(descriptor.getConstructor()); + changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); setConfigurationData(changeTemplateInstance); @@ -62,8 +66,8 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) } @SuppressWarnings({"unchecked", "rawtypes"}) - protected void setTemplateData(ExecutionRuntime executionRuntime, Object instance) { - AbstractSteppableTemplate steppableTemplate = (AbstractSteppableTemplate) instance; + protected void setTemplateData(ExecutionRuntime executionRuntime, AbstractSteppableTemplate instance) { + Object stepsData = descriptor.getSteps(); if (stepsData != null) { @@ -71,12 +75,12 @@ protected void setTemplateData(ExecutionRuntime executionRuntime, Object instanc List convertedSteps = convertToTemplateSteps( stepsData, - steppableTemplate.getApplyPayloadClass(), - steppableTemplate.getRollbackPayloadClass() + instance.getApplyPayloadClass(), + instance.getRollbackPayloadClass() ); - Method setStepsMethod = getSetterMethod(steppableTemplate.getClass(), "setSteps"); - executionRuntime.executeMethodWithParameters(steppableTemplate, setStepsMethod, convertedSteps); + Method setStepsMethod = getSetterMethod(instance.getClass(), "setSteps"); + executionRuntime.executeMethodWithParameters(instance, setStepsMethod, convertedSteps); } else { logger.warn("No 'steps' section provided for steppable template-based change[{}]", descriptor.getId()); } From 2d6425649743f9f3e2a75c2e1e5617538fdb7d0e Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 10:57:02 +0000 Subject: [PATCH 05/19] refactor: wip(4) --- .../api/template/AbstractSimpleTemplate.java | 17 ------------ .../template/AbstractSteppableTemplate.java | 13 +++------ .../SteppableTemplateExecutableTask.java | 15 +++++------ .../internal/util/NotThreadSafe.java | 27 +++++++++++++++++++ 4 files changed, 37 insertions(+), 35 deletions(-) create mode 100644 utils/general-util/src/main/java/io/flamingock/internal/util/NotThreadSafe.java diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java index 3642a9b35..2666f748d 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java @@ -53,23 +53,6 @@ public void setStep(TemplateStep step) { this.step = step; } - /** - * Returns the step containing the apply and optional rollback payloads. - * - * @return the template step, or null if not set - */ - public TemplateStep getStep() { - return step; - } - - /** - * Checks if this template has a step set. - * - * @return true if a step is set - */ - public boolean hasStep() { - return step != null; - } /** * Convenience method to get the apply payload from the step. diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java index 310188031..5a0a4f906 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java @@ -15,6 +15,8 @@ */ package io.flamingock.api.template; +import io.flamingock.internal.util.NotThreadSafe; + import java.util.List; /** @@ -60,10 +62,12 @@ * @param the type of the apply payload for each step * @param the type of the rollback payload for each step */ +@NotThreadSafe public abstract class AbstractSteppableTemplate extends AbstractChangeTemplate { protected List> steps; + protected int atStep = -1; public AbstractSteppableTemplate(Class... additionalReflectiveClass) { super(additionalReflectiveClass); @@ -78,13 +82,4 @@ public void setSteps(List> steps) { this.steps = steps; } - /** - * Returns the list of steps. - * - * @return the list of template steps, or null if not set - */ - public List> getSteps() { - return steps; - } - } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index ba7748b8a..c6622d0c5 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -15,9 +15,7 @@ */ package io.flamingock.internal.core.task.executable; -import io.flamingock.api.template.AbstractSimpleTemplate; import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.template.ChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; @@ -50,22 +48,21 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - AbstractSteppableTemplate changeTemplateInstance = (AbstractSteppableTemplate) + AbstractSteppableTemplate instance = (AbstractSteppableTemplate) executionRuntime.getInstance(descriptor.getConstructor()); - changeTemplateInstance.setTransactional(descriptor.isTransactional()); - changeTemplateInstance.setChangeId(descriptor.getId()); - setConfigurationData(changeTemplateInstance); + instance.setTransactional(descriptor.isTransactional()); + instance.setChangeId(descriptor.getId()); + setConfigurationData(instance); - setTemplateData(executionRuntime, changeTemplateInstance); + setTemplateData(executionRuntime, instance); - executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); } catch (Throwable ex) { throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); } } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({"rawtypes"}) protected void setTemplateData(ExecutionRuntime executionRuntime, AbstractSteppableTemplate instance) { Object stepsData = descriptor.getSteps(); diff --git a/utils/general-util/src/main/java/io/flamingock/internal/util/NotThreadSafe.java b/utils/general-util/src/main/java/io/flamingock/internal/util/NotThreadSafe.java new file mode 100644 index 000000000..241d868b2 --- /dev/null +++ b/utils/general-util/src/main/java/io/flamingock/internal/util/NotThreadSafe.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface NotThreadSafe { + +} From 6410e4f17b46374f2c9cd41c80dc455706742eb9 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 12:29:13 +0000 Subject: [PATCH 06/19] refactor: handling multiple steps --- .../api/template/AbstractChangeTemplate.java | 13 +++++ .../api/template/AbstractSimpleTemplate.java | 58 +------------------ .../template/AbstractSteppableTemplate.java | 24 ++++++-- .../api/template/ChangeTemplate.java | 4 ++ .../SimpleTemplateExecutableTask.java | 3 +- .../SteppableTemplateExecutableTask.java | 5 ++ 6 files changed, 44 insertions(+), 63 deletions(-) diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java index a148d3a4e..77b5222a5 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java @@ -42,7 +42,10 @@ public abstract class AbstractChangeTemplate rollbackPayloadClass; protected String changeId; protected boolean isTransactional; + protected SHARED_CONFIGURATION_FIELD configuration; + protected APPLY_FIELD applyPayload; + protected ROLLBACK_FIELD rollbackPayload; private final Set> additionalReflectiveClasses; @@ -111,6 +114,16 @@ public void setConfiguration(SHARED_CONFIGURATION_FIELD configuration) { this.configuration = configuration; } + @Override + public void setApplyPayload(APPLY_FIELD applyPayload) { + this.applyPayload = applyPayload; + } + + @Override + public void setRollbackPayload(ROLLBACK_FIELD rollbackPayload) { + this.rollbackPayload = rollbackPayload; + } + @Override public Class getConfigurationClass() { return configurationClass; diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java index 2666f748d..af9f49899 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java @@ -15,69 +15,13 @@ */ package io.flamingock.api.template; -/** - * Abstract base class for templates with a single apply/rollback step. - * - *

Use this class when your template processes a single operation that may have - * an optional rollback. The YAML structure for this template type is: - * - *

{@code
- * id: create-users-table
- * template: SqlTemplate
- * apply: "CREATE TABLE users ..."
- * rollback: "DROP TABLE users"
- * }
- * - *

The framework will automatically create a {@link TemplateStep} from the - * apply/rollback fields in the YAML and inject it via {@link #setStep}. - * - * @param the type of shared configuration - * @param the type of the apply payload - * @param the type of the rollback payload - */ + public abstract class AbstractSimpleTemplate extends AbstractChangeTemplate { - protected TemplateStep step; public AbstractSimpleTemplate(Class... additionalReflectiveClass) { super(additionalReflectiveClass); } - /** - * Sets the step containing the apply and optional rollback payloads. - * - * @param step the template step - */ - public void setStep(TemplateStep step) { - this.step = step; - } - - - /** - * Convenience method to get the apply payload from the step. - * - * @return the apply payload, or null if no step is set - */ - public APPLY getApply() { - return step != null ? step.getApply() : null; - } - - /** - * Convenience method to get the rollback payload from the step. - * - * @return the rollback payload, or null if no step is set or no rollback defined - */ - public ROLLBACK getRollback() { - return step != null ? step.getRollback() : null; - } - - /** - * Checks if this template has a rollback payload defined. - * - * @return true if a step is set and it has a rollback payload - */ - public boolean hasRollback() { - return step != null && step.hasRollback(); - } } diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java index 5a0a4f906..553c842ce 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java @@ -17,6 +17,7 @@ import io.flamingock.internal.util.NotThreadSafe; +import java.util.ArrayList; import java.util.List; /** @@ -59,15 +60,15 @@ * * * @param the type of shared configuration - * @param the type of the apply payload for each step - * @param the type of the rollback payload for each step + * @param the type of the apply payload for each step + * @param the type of the rollback payload for each step */ @NotThreadSafe public abstract class AbstractSteppableTemplate extends AbstractChangeTemplate { - protected List> steps; - protected int atStep = -1; + private List> steps = new ArrayList<>(); + private int atStep = -1; public AbstractSteppableTemplate(Class... additionalReflectiveClass) { super(additionalReflectiveClass); @@ -78,8 +79,21 @@ public AbstractSteppableTemplate(Class... additionalReflectiveClass) { * * @param steps the list of template steps */ - public void setSteps(List> steps) { + public final void setSteps(List> steps) { this.steps = steps; } + public final boolean advance() { + if (atStep + 1 >= steps.size()) { + return false; + } + atStep++; + TemplateStep currentStep = steps.get(atStep); + this.setApplyPayload(currentStep.getApply()); + return true; + } + + + + } diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java index c99e20d1f..9ac70af61 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java @@ -35,6 +35,10 @@ public interface ChangeTemplate getConfigurationClass(); Class getApplyPayloadClass(); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 6536e4ced..4dbe53d38 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -72,7 +72,8 @@ protected void setTemplateData(AbstractSimpleTemplate instance) { instance.getApplyPayloadClass(), instance.getRollbackPayloadClass() ); - instance.setStep(step); + instance.setApplyPayload(step.getApply()); + instance.setRollbackPayload(step.getRollback()); } else { logger.warn("No 'apply' section provided for simple template-based change[{}]", descriptor.getId()); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index c6622d0c5..5723f194b 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -56,6 +56,9 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) setConfigurationData(instance); setTemplateData(executionRuntime, instance); + while (instance.advance()) { + executionRuntime.executeMethodWithInjectedDependencies(instance, method); + } } catch (Throwable ex) { throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); @@ -78,8 +81,10 @@ protected void setTemplateData(ExecutionRuntime executionRuntime, AbstractSteppa Method setStepsMethod = getSetterMethod(instance.getClass(), "setSteps"); executionRuntime.executeMethodWithParameters(instance, setStepsMethod, convertedSteps); + } else { logger.warn("No 'steps' section provided for steppable template-based change[{}]", descriptor.getId()); + //TODO this should throw an exception } } From 1c9809e90a676a28f7e4f38cbdc6641f2fe86f71 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 14:03:24 +0000 Subject: [PATCH 07/19] refactor: refactor typeSafety --- .../SimpleTemplateExecutableTask.java | 39 ++----- .../SteppableTemplateExecutableTask.java | 73 +----------- .../TemplateExecutableTaskBuilder.java | 6 +- .../loaded/SimpleTemplateLoadedChange.java | 28 +++-- .../loaded/SteppableTemplateLoadedChange.java | 11 +- .../loaded/TemplateLoadedTaskBuilder.java | 110 +++++++++++++++++- .../SimpleTemplateLoadedTaskBuilderTest.java | 23 ++-- ...teppableTemplateLoadedTaskBuilderTest.java | 45 +++---- 8 files changed, 180 insertions(+), 155 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 4dbe53d38..f8bfd62f7 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -16,12 +16,10 @@ package io.flamingock.internal.core.task.executable; import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SimpleTemplateLoadedChange; -import io.flamingock.internal.util.FileUtil; import java.lang.reflect.Method; @@ -50,7 +48,6 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); setConfigurationData(changeTemplateInstance); - setTemplateData(changeTemplateInstance); executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); @@ -61,38 +58,16 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) @SuppressWarnings({"unchecked", "rawtypes"}) protected void setTemplateData(AbstractSimpleTemplate instance) { - Object applyData = descriptor.getApply(); - - if (applyData != null) { - logger.debug("Setting step for simple template change[{}]", descriptor.getId()); - - TemplateStep step = convertToTemplateStep( - applyData, - descriptor.getRollback(), - instance.getApplyPayloadClass(), - instance.getRollbackPayloadClass() - ); - instance.setApplyPayload(step.getApply()); - instance.setRollbackPayload(step.getRollback()); + Object applyPayload = descriptor.getApplyPayload(); + Object rollbackPayload = descriptor.getRollbackPayload(); + if (applyPayload != null) { + logger.debug("Setting payloads for simple template change[{}]", descriptor.getId()); + instance.setApplyPayload(applyPayload); + instance.setRollbackPayload(rollbackPayload); } else { - logger.warn("No 'apply' section provided for simple template-based change[{}]", descriptor.getId()); + logger.warn("No apply payload provided for simple template-based change[{}]", descriptor.getId()); } } - - @SuppressWarnings({"unchecked", "rawtypes"}) - protected TemplateStep convertToTemplateStep(Object applyData, - Object rollbackData, - Class applyClass, - Class rollbackClass) { - TemplateStep step = new TemplateStep(); - step.setApply(FileUtil.getFromMap(applyClass, applyData)); - if (rollbackData != null && Void.class != rollbackClass) { - step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); - } - - return step; - } - } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index 5723f194b..67d5e20bb 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -21,12 +21,9 @@ import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; -import io.flamingock.internal.util.FileUtil; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * Executable task for steppable templates (multiple steps). @@ -55,7 +52,8 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) instance.setChangeId(descriptor.getId()); setConfigurationData(instance); - setTemplateData(executionRuntime, instance); + + List> steps = descriptor.getSteps(); while (instance.advance()) { executionRuntime.executeMethodWithInjectedDependencies(instance, method); } @@ -65,71 +63,4 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) } } - @SuppressWarnings({"rawtypes"}) - protected void setTemplateData(ExecutionRuntime executionRuntime, AbstractSteppableTemplate instance) { - - Object stepsData = descriptor.getSteps(); - - if (stepsData != null) { - logger.debug("Setting steps for steppable template change[{}]", descriptor.getId()); - - List convertedSteps = convertToTemplateSteps( - stepsData, - instance.getApplyPayloadClass(), - instance.getRollbackPayloadClass() - ); - - Method setStepsMethod = getSetterMethod(instance.getClass(), "setSteps"); - executionRuntime.executeMethodWithParameters(instance, setStepsMethod, convertedSteps); - - } else { - logger.warn("No 'steps' section provided for steppable template-based change[{}]", descriptor.getId()); - //TODO this should throw an exception - } - } - - - /** - * Converts raw step data (List of Maps from YAML) to a list of TemplateStep objects. - * - * @param stepsData the raw steps data (expected to be a List of Maps) - * @param applyClass the class type for apply payloads - * @param rollbackClass the class type for rollback payloads - * @return list of converted TemplateStep objects - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - protected List convertToTemplateSteps(Object stepsData, - Class applyClass, - Class rollbackClass) { - List result = new ArrayList<>(); - - if (!(stepsData instanceof List)) { - logger.warn("Steps data is not a List, ignoring"); - return result; - } - - List stepsList = (List) stepsData; - for (Object stepItem : stepsList) { - if (stepItem instanceof Map) { - Map stepMap = (Map) stepItem; - TemplateStep step = new TemplateStep(); - - Object applyData = stepMap.get("apply"); - if (applyData != null && Void.class != applyClass) { - step.setApply(FileUtil.getFromMap(applyClass, applyData)); - } - - Object rollbackData = stepMap.get("rollback"); - if (rollbackData != null && Void.class != rollbackClass) { - step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackData)); - } - - result.add(step); - } else if (stepItem instanceof TemplateStep) { - result.add((TemplateStep) stepItem); - } - } - - return result; - } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java index 8459ba450..83a570bfa 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java @@ -79,18 +79,18 @@ public ExecutableTask build() { if (loadedTask instanceof SimpleTemplateLoadedChange) { SimpleTemplateLoadedChange simple = (SimpleTemplateLoadedChange) loadedTask; // Only include rollback method if rollback data is present - if (simple.getRollback() != null) { + if (simple.hasRollback()) { if (rollbackMethod != null) { logger.trace("Change[{}] provides rollback in configuration", loadedTask.getId()); } else { - logger.warn("Change[{}] provides rollback in configuration, but based on a template[{}] not supporting manual rollback", + logger.warn("Change[{}] provides rollback in configuration, but template[{}] doesn't support manual rollback", loadedTask.getId(), loadedTask.getSource() ); } } else { if (rollbackMethod != null) { - logger.warn("Change[{}] does not provide rollback, but based on a template[{}] support manual rollback", + logger.warn("Change[{}] does not provide rollback, but template[{}] supports manual rollback", loadedTask.getId(), loadedTask.getSource() ); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java index 7ef23adeb..3df7e8313 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java @@ -26,11 +26,15 @@ /** * Loaded change for simple templates (single apply/rollback step). * Used for templates extending {@link io.flamingock.api.template.AbstractSimpleTemplate}. + *

+ * The payloads are converted from raw YAML data (Object/Map) to typed values + * at load time, enabling early validation and cleaner executable tasks. */ public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { - private final Object apply; - private final Object rollback; + // Already converted to typed payload (no longer raw Object from YAML) + private final Object applyPayload; + private final Object rollbackPayload; SimpleTemplateLoadedChange(String changeFileName, String id, @@ -43,20 +47,24 @@ public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { boolean runAlways, boolean systemTask, Object configuration, - Object apply, - Object rollback, + Object applyPayload, + Object rollbackPayload, TargetSystemDescriptor targetSystem, RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); - this.apply = apply; - this.rollback = rollback; + this.applyPayload = applyPayload; + this.rollbackPayload = rollbackPayload; } - public Object getApply() { - return apply; + public Object getApplyPayload() { + return applyPayload; } - public Object getRollback() { - return rollback; + public Object getRollbackPayload() { + return rollbackPayload; + } + + public boolean hasRollback() { + return rollbackPayload != null; } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java index 4133fbf9c..af15d2891 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java @@ -16,7 +16,7 @@ package io.flamingock.internal.core.task.loaded; import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -26,10 +26,13 @@ /** * Loaded change for steppable templates (multiple steps). * Used for templates extending {@link io.flamingock.api.template.AbstractSteppableTemplate}. + *

+ * The steps are converted from raw YAML data (List of Maps) to typed TemplateStep objects + * at load time, enabling early validation and cleaner executable tasks. */ public class SteppableTemplateLoadedChange extends AbstractTemplateLoadedChange { - private final Object steps; + private final List> steps; SteppableTemplateLoadedChange(String changeFileName, String id, @@ -42,14 +45,14 @@ public class SteppableTemplateLoadedChange extends AbstractTemplateLoadedChange boolean runAlways, boolean systemTask, Object configuration, - Object steps, + List> steps, TargetSystemDescriptor targetSystem, RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); this.steps = steps; } - public Object getSteps() { + public List> getSteps() { return steps; } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index a7fbfa44a..e479e7920 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -17,19 +17,23 @@ import io.flamingock.api.template.AbstractSimpleTemplate; import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.api.template.ChangeTemplate; -import io.flamingock.internal.common.core.preview.PreviewConstructor; -import io.flamingock.internal.common.core.template.ChangeTemplateManager; +import io.flamingock.api.template.TemplateStep; +import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.preview.AbstractPreviewTask; import io.flamingock.internal.common.core.preview.TemplatePreviewChange; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; +import io.flamingock.internal.common.core.template.ChangeTemplateManager; +import io.flamingock.internal.util.FileUtil; +import io.flamingock.internal.util.Pair; import io.flamingock.internal.util.ReflectionUtil; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; //TODO how to set transactional and runAlways @@ -156,6 +160,10 @@ public AbstractTemplateLoadedChange build() { @SuppressWarnings("unchecked") Class> steppableTemplateClass = (Class>) templateClass.asSubclass(AbstractSteppableTemplate.class); + + // Convert steps at load time + List> convertedSteps = convertSteps(constructor, steps); + return new SteppableTemplateLoadedChange( fileName, id, @@ -168,7 +176,7 @@ public AbstractTemplateLoadedChange build() { runAlways, system, configuration, - steps, + convertedSteps, targetSystem, recovery); } else { @@ -177,6 +185,9 @@ public AbstractTemplateLoadedChange build() { Class> simpleTemplateClass = (Class>) templateClass.asSubclass(AbstractSimpleTemplate.class); + // Convert apply/rollback to typed payloads at load time + Pair convertedPayloads = convertPayloads(constructor, apply, rollback); + return new SimpleTemplateLoadedChange( fileName, id, @@ -189,14 +200,101 @@ public AbstractTemplateLoadedChange build() { runAlways, system, configuration, - apply, - rollback, + convertedPayloads.getFirst(), + convertedPayloads.getSecond(), targetSystem, recovery); } } + /** + * Converts raw apply/rollback data to typed payloads for simple templates. + * Returns Pair. + */ + private Pair convertPayloads(Constructor constructor, Object applyData, Object rollbackData) { + if (applyData == null) { + return new Pair<>(null, null); + } + + // Instantiate template temporarily to get payload types + AbstractSimpleTemplate templateInstance; + try { + templateInstance = (AbstractSimpleTemplate) constructor.newInstance(); + } catch (Exception e) { + throw new FlamingockException("Failed to instantiate template for type resolution: " + e.getMessage(), e); + } + + Class applyClass = templateInstance.getApplyPayloadClass(); + Class rollbackClass = templateInstance.getRollbackPayloadClass(); + + Object applyPayload = FileUtil.getFromMap(applyClass, applyData); + Object rollbackPayload = null; + + if (rollbackData != null && Void.class != rollbackClass) { + rollbackPayload = FileUtil.getFromMap(rollbackClass, rollbackData); + } + + return new Pair<>(applyPayload, rollbackPayload); + } + + /** + * Converts raw steps data from YAML to typed TemplateStep objects. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private List> convertSteps(Constructor constructor, Object stepsData) { + if (stepsData == null) { + return null; + } + + if (!(stepsData instanceof List)) { + throw new FlamingockException(String.format( + "Steps must be a List for steppable template change[%s], but got: %s", + id, stepsData.getClass().getSimpleName())); + } + + List stepsList = (List) stepsData; + if (stepsList.isEmpty()) { + return Collections.emptyList(); + } + + // Instantiate template temporarily to get payload types + AbstractSteppableTemplate templateInstance; + try { + templateInstance = (AbstractSteppableTemplate) constructor.newInstance(); + } catch (Exception e) { + throw new FlamingockException("Failed to instantiate template for type resolution: " + e.getMessage(), e); + } + + Class applyClass = templateInstance.getApplyPayloadClass(); + Class rollbackClass = templateInstance.getRollbackPayloadClass(); + + List> result = new ArrayList<>(); + + for (Object stepItem : stepsList) { + if (stepItem instanceof Map) { + Map stepMap = (Map) stepItem; + TemplateStep step = new TemplateStep(); + + Object applyItemData = stepMap.get("apply"); + if (applyItemData != null && Void.class != applyClass) { + step.setApply(FileUtil.getFromMap(applyClass, applyItemData)); + } + + Object rollbackItemData = stepMap.get("rollback"); + if (rollbackItemData != null && Void.class != rollbackClass) { + step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackItemData)); + } + + result.add(step); + } else if (stepItem instanceof TemplateStep) { + result.add((TemplateStep) stepItem); + } + } + + return result; + } + private TemplateLoadedTaskBuilder setPreview(TemplatePreviewChange preview) { setFileName(preview.getFileName()); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java index e09cc39e5..f485a23cc 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java @@ -67,9 +67,9 @@ void shouldBuildWithOrderInContentWhenOrderInContentPresentAndNoOrderInFileName( .setRunAlways(false) .setTransactional(true) .setSystem(false) - .setConfiguration(new Object()) - .setApply(new Object()) - .setRollback(new Object()); + .setConfiguration("testConfig") + .setApply("applyPayload") + .setRollback("rollbackPayload"); builder.setProfiles(Arrays.asList("test")); // When @@ -80,6 +80,11 @@ void shouldBuildWithOrderInContentWhenOrderInContentPresentAndNoOrderInFileName( assertEquals("001", result.getOrder().orElse(null)); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); + // Verify typed payloads are stored + SimpleTemplateLoadedChange simpleResult = (SimpleTemplateLoadedChange) result; + assertNotNull(simpleResult.getApplyPayload()); + assertNotNull(simpleResult.getRollbackPayload()); + assertTrue(simpleResult.hasRollback()); } } @@ -98,9 +103,9 @@ void shouldBuildWithOrderFromFileNameWhenOrderInContentIsNullAndOrderInFileNameI .setRunAlways(false) .setTransactional(true) .setSystem(false) - .setConfiguration(new Object()) - .setApply(new Object()) - .setRollback(new Object()); + .setConfiguration("testConfig") + .setApply("applyPayload") + .setRollback("rollbackPayload"); builder.setProfiles(Arrays.asList("test")); // When @@ -130,9 +135,9 @@ void shouldBuildWithOrderInContentWhenOrderInContentMatchesOrderInFileName() { builder.setProfiles(Arrays.asList("test")); builder.setTransactional(true) .setSystem(false) - .setConfiguration(new Object()) - .setApply(new Object()) - .setRollback(new Object()); + .setConfiguration("testConfig") + .setApply("applyPayload") + .setRollback("rollbackPayload"); // When AbstractTemplateLoadedChange result = builder.build(); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index 14b8fb815..372e75df5 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -15,10 +15,11 @@ */ package io.flamingock.internal.core.task.loaded; +import io.flamingock.api.annotations.Apply; +import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.template.ChangeTemplateManager; -import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.annotations.Apply; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -85,9 +86,11 @@ void shouldBuildWithStepsWhenStepsProvided() { assertNotNull(result); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); - assertNotNull(steppableResult.getSteps()); - // Steps are stored as raw Object - conversion happens at execution time - assertEquals(rawSteps, steppableResult.getSteps()); + // Steps are now converted to List at load time + List> steps = steppableResult.getSteps(); + assertNotNull(steps); + assertEquals(2, steps.size()); + assertInstanceOf(TemplateStep.class, steps.get(0)); } } @@ -184,8 +187,9 @@ void shouldBuildWithEmptyStepsList() { SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); - assertNotNull(steppableResult.getSteps()); - assertTrue(((List) steppableResult.getSteps()).isEmpty()); + List> steps = steppableResult.getSteps(); + assertNotNull(steps); + assertTrue(steps.isEmpty()); } } @@ -219,14 +223,14 @@ void shouldBuildWithMultipleSteps() { SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("multi-step-change", result.getId()); - assertNotNull(steppableResult.getSteps()); - @SuppressWarnings("unchecked") - List> resultSteps = (List>) steppableResult.getSteps(); - assertEquals(3, resultSteps.size()); - // Verify steps are preserved in order - assertEquals("createCollection", resultSteps.get(0).get("apply")); - assertEquals("insertDocument", resultSteps.get(1).get("apply")); - assertEquals("createIndex", resultSteps.get(2).get("apply")); + // Steps are now converted to List at load time + List> steps = steppableResult.getSteps(); + assertNotNull(steps); + assertEquals(3, steps.size()); + // Verify steps are preserved in order - payloads are now typed objects + assertEquals("createCollection", steps.get(0).getApply()); + assertEquals("insertDocument", steps.get(1).getApply()); + assertEquals("createIndex", steps.get(2).getApply()); } } @@ -282,11 +286,11 @@ void shouldBuildWithStepsHavingOnlyApply() { assertInstanceOf(SteppableTemplateLoadedChange.class, result); SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); - @SuppressWarnings("unchecked") - List> resultSteps = (List>) steppableResult.getSteps(); - assertEquals(2, resultSteps.size()); - assertNull(resultSteps.get(0).get("rollback")); - assertNull(resultSteps.get(1).get("rollback")); + // Steps are now converted to List at load time + List> steps = steppableResult.getSteps(); + assertEquals(2, steps.size()); + assertNull(steps.get(0).getRollback()); + assertNull(steps.get(1).getRollback()); } } @@ -313,6 +317,7 @@ void shouldBuildWithNullSteps() { SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); + // Null steps remain null after conversion assertNull(steppableResult.getSteps()); } } From 3ffb65da455d7d5623bfd2aa29cadf2fd6b402c1 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 15:04:18 +0000 Subject: [PATCH 08/19] refactor: refactor typeSafety(2) --- .../AbstractTemplateExecutableTask.java | 2 +- .../SimpleTemplateExecutableTask.java | 4 +-- .../SteppableTemplateExecutableTask.java | 18 ++++++++--- .../builder/ExecutableTaskBuilder.java | 2 +- .../TemplateExecutableTaskBuilder.java | 14 ++++---- .../loaded/AbstractTemplateLoadedChange.java | 18 +++++++---- .../loaded/SimpleTemplateLoadedChange.java | 24 ++++++++------ .../loaded/SteppableTemplateLoadedChange.java | 17 ++++++---- .../loaded/TemplateLoadedTaskBuilder.java | 30 +++++++++-------- .../SimpleTemplateLoadedTaskBuilderTest.java | 8 ++--- ...teppableTemplateLoadedTaskBuilderTest.java | 32 +++++++++---------- 11 files changed, 98 insertions(+), 71 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java index 65cca3039..bae296e94 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java @@ -33,7 +33,7 @@ * * @param the type of template loaded change */ -public abstract class AbstractTemplateExecutableTask extends ReflectionExecutableTask { +public abstract class AbstractTemplateExecutableTask> extends ReflectionExecutableTask { protected final Logger logger = FlamingockLoggerFactory.getLogger("TemplateTask"); public AbstractTemplateExecutableTask(String stageName, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index f8bfd62f7..cd85c735e 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -27,10 +27,10 @@ * Executable task for simple templates (single apply/rollback step). * Handles templates extending {@link AbstractSimpleTemplate}. */ -public class SimpleTemplateExecutableTask extends AbstractTemplateExecutableTask { +public class SimpleTemplateExecutableTask extends AbstractTemplateExecutableTask> { public SimpleTemplateExecutableTask(String stageName, - SimpleTemplateLoadedChange descriptor, + SimpleTemplateLoadedChange descriptor, ChangeAction action, Method executionMethod, Method rollbackMethod) { diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index 67d5e20bb..b8fb01b6a 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -29,10 +29,10 @@ * Executable task for steppable templates (multiple steps). * Handles templates extending {@link AbstractSteppableTemplate}. */ -public class SteppableTemplateExecutableTask extends AbstractTemplateExecutableTask { +public class SteppableTemplateExecutableTask extends AbstractTemplateExecutableTask> { public SteppableTemplateExecutableTask(String stageName, - SteppableTemplateLoadedChange descriptor, + SteppableTemplateLoadedChange descriptor, ChangeAction action, Method executionMethod, Method rollbackMethod) { @@ -52,8 +52,7 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) instance.setChangeId(descriptor.getId()); setConfigurationData(instance); - - List> steps = descriptor.getSteps(); + setStepsData(instance, descriptor.getSteps()); while (instance.advance()) { executionRuntime.executeMethodWithInjectedDependencies(instance, method); } @@ -63,4 +62,15 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) } } + /** + * Sets the steps data on the template instance. + * Uses a helper method to handle the generic type capture properly. + */ + @SuppressWarnings("unchecked") + private void setStepsData(AbstractSteppableTemplate instance, + List> steps) { + // Safe cast: the steps were created using the template's type information + instance.setSteps((List>) steps); + } + } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java index d8669ed49..bc156115f 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/ExecutableTaskBuilder.java @@ -43,7 +43,7 @@ static ExecutableTaskBuilder getInstance(AbstractLoadedTask loadedTask) { if(TemplateExecutableTaskBuilder.supports(loadedTask)) { TemplateExecutableTaskBuilder templateBuilder = TemplateExecutableTaskBuilder.getInstance(); - AbstractTemplateLoadedChange castedTask = templateBuilder.cast(loadedTask); + AbstractTemplateLoadedChange castedTask = templateBuilder.cast(loadedTask); return templateBuilder.setLoadedTask(castedTask); } else if(CodeExecutableTaskBuilder.supports(loadedTask)) { diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java index 83a570bfa..fe7126a1f 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java @@ -32,13 +32,13 @@ /** * Factory for Change classes */ -public class TemplateExecutableTaskBuilder implements ExecutableTaskBuilder { +public class TemplateExecutableTaskBuilder implements ExecutableTaskBuilder> { private final static Logger logger = FlamingockLoggerFactory.getLogger("TemplateBuilder"); private static final TemplateExecutableTaskBuilder instance = new TemplateExecutableTaskBuilder(); private String stageName; private ChangeAction changeAction; - private AbstractTemplateLoadedChange loadedTask; + private AbstractTemplateLoadedChange loadedTask; static TemplateExecutableTaskBuilder getInstance() { return instance; @@ -49,12 +49,12 @@ public static boolean supports(AbstractLoadedTask loadedTask) { } @Override - public AbstractTemplateLoadedChange cast(AbstractLoadedTask loadedTask) { - return (AbstractTemplateLoadedChange) loadedTask; + public AbstractTemplateLoadedChange cast(AbstractLoadedTask loadedTask) { + return (AbstractTemplateLoadedChange) loadedTask; } @Override - public TemplateExecutableTaskBuilder setLoadedTask(AbstractTemplateLoadedChange loadedTask) { + public TemplateExecutableTaskBuilder setLoadedTask(AbstractTemplateLoadedChange loadedTask) { this.loadedTask = loadedTask; return this; } @@ -77,7 +77,7 @@ public ExecutableTask build() { Method rollbackMethod = loadedTask.getRollbackMethod().orElse(null); if (loadedTask instanceof SimpleTemplateLoadedChange) { - SimpleTemplateLoadedChange simple = (SimpleTemplateLoadedChange) loadedTask; + SimpleTemplateLoadedChange simple = (SimpleTemplateLoadedChange) loadedTask; // Only include rollback method if rollback data is present if (simple.hasRollback()) { if (rollbackMethod != null) { @@ -105,7 +105,7 @@ public ExecutableTask build() { rollbackMethod ); } else if (loadedTask instanceof SteppableTemplateLoadedChange) { - SteppableTemplateLoadedChange steppable = (SteppableTemplateLoadedChange) loadedTask; + SteppableTemplateLoadedChange steppable = (SteppableTemplateLoadedChange) loadedTask; if (rollbackMethod != null) { logger.trace("Change[{}] is a steppable template with rollback method", loadedTask.getId()); } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java index fa713d33f..d9e8d727b 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java @@ -31,23 +31,27 @@ * Abstract base class for template-based loaded changes. * Contains common fields and methods shared by both SimpleTemplateLoadedChange * and SteppableTemplateLoadedChange. + * + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type */ -public abstract class AbstractTemplateLoadedChange extends AbstractLoadedChange { +public abstract class AbstractTemplateLoadedChange extends AbstractLoadedChange { private final List profiles; - private final Object configuration; + private final CONFIG configuration; protected AbstractTemplateLoadedChange(String changeFileName, String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, boolean runAlways, boolean systemTask, - Object configuration, + CONFIG configuration, TargetSystemDescriptor targetSystem, RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, runAlways, transactional, systemTask, targetSystem, recovery, false); @@ -56,7 +60,7 @@ protected AbstractTemplateLoadedChange(String changeFileName, this.configuration = configuration; } - public Object getConfiguration() { + public CONFIG getConfiguration() { return configuration; } @@ -65,8 +69,8 @@ public List getProfiles() { } @SuppressWarnings("unchecked") - public Class> getTemplateClass() { - return (Class>) this.getImplementationClass(); + public Class> getTemplateClass() { + return (Class>) this.getImplementationClass(); } @Override diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java index 3df7e8313..5ec4ea209 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java @@ -16,7 +16,6 @@ package io.flamingock.internal.core.task.loaded; import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.ChangeTemplate; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -29,26 +28,31 @@ *

* The payloads are converted from raw YAML data (Object/Map) to typed values * at load time, enabling early validation and cleaner executable tasks. + * + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type */ -public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { +public class SimpleTemplateLoadedChange + extends AbstractTemplateLoadedChange { // Already converted to typed payload (no longer raw Object from YAML) - private final Object applyPayload; - private final Object rollbackPayload; + private final APPLY applyPayload; + private final ROLLBACK rollbackPayload; SimpleTemplateLoadedChange(String changeFileName, String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, boolean runAlways, boolean systemTask, - Object configuration, - Object applyPayload, - Object rollbackPayload, + CONFIG configuration, + APPLY applyPayload, + ROLLBACK rollbackPayload, TargetSystemDescriptor targetSystem, RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); @@ -56,11 +60,11 @@ public class SimpleTemplateLoadedChange extends AbstractTemplateLoadedChange { this.rollbackPayload = rollbackPayload; } - public Object getApplyPayload() { + public APPLY getApplyPayload() { return applyPayload; } - public Object getRollbackPayload() { + public ROLLBACK getRollbackPayload() { return rollbackPayload; } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java index af15d2891..ec85d151b 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java @@ -29,30 +29,35 @@ *

* The steps are converted from raw YAML data (List of Maps) to typed TemplateStep objects * at load time, enabling early validation and cleaner executable tasks. + * + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type */ -public class SteppableTemplateLoadedChange extends AbstractTemplateLoadedChange { +public class SteppableTemplateLoadedChange + extends AbstractTemplateLoadedChange { - private final List> steps; + private final List> steps; SteppableTemplateLoadedChange(String changeFileName, String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, boolean runAlways, boolean systemTask, - Object configuration, - List> steps, + CONFIG configuration, + List> steps, TargetSystemDescriptor targetSystem, RecoveryDescriptor recovery) { super(changeFileName, id, order, author, templateClass, constructor, profiles, transactional, runAlways, systemTask, configuration, targetSystem, recovery); this.steps = steps; } - public List> getSteps() { + public List> getSteps() { return steps; } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index e479e7920..60686e5e8 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -37,7 +37,7 @@ //TODO how to set transactional and runAlways -public class TemplateLoadedTaskBuilder implements LoadedTaskBuilder { +public class TemplateLoadedTaskBuilder implements LoadedTaskBuilder> { private String fileName; private String id; @@ -147,7 +147,8 @@ public TemplateLoadedTaskBuilder setSteps(Object steps) { } @Override - public AbstractTemplateLoadedChange build() { + @SuppressWarnings("unchecked") + public AbstractTemplateLoadedChange build() { // boolean isTaskTransactional = true;//TODO implement this. isTaskTransactionalAccordingTemplate(templateSpec); Class> templateClass = ChangeTemplateManager.getTemplate(templateName) .orElseThrow(()-> new FlamingockException(String.format("Template[%s] not found. This is probably because template's name is wrong or template's library not imported", templateName))); @@ -155,16 +156,19 @@ public AbstractTemplateLoadedChange build() { Constructor constructor = ReflectionUtil.getDefaultConstructor(templateClass); // Determine template type and build appropriate loaded change + // Note: Due to type erasure, we use Object bounds at construction time. + // The actual type safety comes from the conversion methods that use reflection + // to determine the real types at runtime. if (AbstractSteppableTemplate.class.isAssignableFrom(templateClass)) { - @SuppressWarnings("unchecked") - Class> steppableTemplateClass = (Class>) + Class> steppableTemplateClass = + (Class>) templateClass.asSubclass(AbstractSteppableTemplate.class); // Convert steps at load time - List> convertedSteps = convertSteps(constructor, steps); + List> convertedSteps = convertSteps(constructor, steps); - return new SteppableTemplateLoadedChange( + return new SteppableTemplateLoadedChange<>( fileName, id, order, @@ -181,14 +185,14 @@ public AbstractTemplateLoadedChange build() { recovery); } else { // Default to SimpleTemplateLoadedChange for AbstractSimpleTemplate and unknown types - @SuppressWarnings("unchecked") - Class> simpleTemplateClass = (Class>) + Class> simpleTemplateClass = + (Class>) templateClass.asSubclass(AbstractSimpleTemplate.class); // Convert apply/rollback to typed payloads at load time Pair convertedPayloads = convertPayloads(constructor, apply, rollback); - return new SimpleTemplateLoadedChange( + return new SimpleTemplateLoadedChange<>( fileName, id, order, @@ -242,7 +246,7 @@ private Pair convertPayloads(Constructor constructor, Object * Converts raw steps data from YAML to typed TemplateStep objects. */ @SuppressWarnings({"unchecked", "rawtypes"}) - private List> convertSteps(Constructor constructor, Object stepsData) { + private List> convertSteps(Constructor constructor, Object stepsData) { if (stepsData == null) { return null; } @@ -269,12 +273,12 @@ private Pair convertPayloads(Constructor constructor, Object Class applyClass = templateInstance.getApplyPayloadClass(); Class rollbackClass = templateInstance.getRollbackPayloadClass(); - List> result = new ArrayList<>(); + List> result = new ArrayList<>(); for (Object stepItem : stepsList) { if (stepItem instanceof Map) { Map stepMap = (Map) stepItem; - TemplateStep step = new TemplateStep(); + TemplateStep step = new TemplateStep<>(); Object applyItemData = stepMap.get("apply"); if (applyItemData != null && Void.class != applyClass) { @@ -288,7 +292,7 @@ private Pair convertPayloads(Constructor constructor, Object result.add(step); } else if (stepItem instanceof TemplateStep) { - result.add((TemplateStep) stepItem); + result.add((TemplateStep) stepItem); } } diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java index f485a23cc..99661d64e 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java @@ -73,7 +73,7 @@ void shouldBuildWithOrderInContentWhenOrderInContentPresentAndNoOrderInFileName( builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SimpleTemplateLoadedChange.class, result); @@ -81,7 +81,7 @@ void shouldBuildWithOrderInContentWhenOrderInContentPresentAndNoOrderInFileName( assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); // Verify typed payloads are stored - SimpleTemplateLoadedChange simpleResult = (SimpleTemplateLoadedChange) result; + SimpleTemplateLoadedChange simpleResult = (SimpleTemplateLoadedChange) result; assertNotNull(simpleResult.getApplyPayload()); assertNotNull(simpleResult.getRollbackPayload()); assertTrue(simpleResult.hasRollback()); @@ -109,7 +109,7 @@ void shouldBuildWithOrderFromFileNameWhenOrderInContentIsNullAndOrderInFileNameI builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SimpleTemplateLoadedChange.class, result); @@ -140,7 +140,7 @@ void shouldBuildWithOrderInContentWhenOrderInContentMatchesOrderInFileName() { .setRollback("rollbackPayload"); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SimpleTemplateLoadedChange.class, result); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index 372e75df5..8986a759d 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -78,16 +78,16 @@ void shouldBuildWithStepsWhenStepsProvided() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); - SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); assertEquals("test-file.yml", result.getFileName()); // Steps are now converted to List at load time - List> steps = steppableResult.getSteps(); + List> steps = steppableResult.getSteps(); assertNotNull(steps); assertEquals(2, steps.size()); assertInstanceOf(TemplateStep.class, steps.get(0)); @@ -118,7 +118,7 @@ void shouldBuildWithOrderInContentForSteppableTemplate() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); @@ -152,7 +152,7 @@ void shouldBuildWithOrderFromFileNameForSteppableTemplate() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); @@ -180,14 +180,14 @@ void shouldBuildWithEmptyStepsList() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); - SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); - List> steps = steppableResult.getSteps(); + List> steps = steppableResult.getSteps(); assertNotNull(steps); assertTrue(steps.isEmpty()); } @@ -216,15 +216,15 @@ void shouldBuildWithMultipleSteps() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); - SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("multi-step-change", result.getId()); // Steps are now converted to List at load time - List> steps = steppableResult.getSteps(); + List> steps = steppableResult.getSteps(); assertNotNull(steps); assertEquals(3, steps.size()); // Verify steps are preserved in order - payloads are now typed objects @@ -280,14 +280,14 @@ void shouldBuildWithStepsHavingOnlyApply() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); - SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); // Steps are now converted to List at load time - List> steps = steppableResult.getSteps(); + List> steps = steppableResult.getSteps(); assertEquals(2, steps.size()); assertNull(steps.get(0).getRollback()); assertNull(steps.get(1).getRollback()); @@ -310,11 +310,11 @@ void shouldBuildWithNullSteps() { builder.setProfiles(Arrays.asList("test")); // When - AbstractTemplateLoadedChange result = builder.build(); + AbstractTemplateLoadedChange result = builder.build(); // Then assertInstanceOf(SteppableTemplateLoadedChange.class, result); - SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; + SteppableTemplateLoadedChange steppableResult = (SteppableTemplateLoadedChange) result; assertNotNull(result); assertEquals("test-id", result.getId()); // Null steps remain null after conversion From 55f961102bae2d073da4e137c0f010aa57a92f0e Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 15:21:25 +0000 Subject: [PATCH 09/19] refactor: refactor typeSafety(3) --- .../AbstractTemplateExecutableTask.java | 14 ++++++---- .../SimpleTemplateExecutableTask.java | 23 +++++++++------ .../SteppableTemplateExecutableTask.java | 28 +++++++++++-------- .../TemplateExecutableTaskBuilder.java | 4 +-- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java index bae296e94..b20f80c15 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/AbstractTemplateExecutableTask.java @@ -31,9 +31,13 @@ * Contains common logic for executing templates, with type-specific data setting * delegated to subclasses. * - * @param the type of template loaded change + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type + * @param the type of template loaded change */ -public abstract class AbstractTemplateExecutableTask> extends ReflectionExecutableTask { +public abstract class AbstractTemplateExecutableTask> extends ReflectionExecutableTask { protected final Logger logger = FlamingockLoggerFactory.getLogger("TemplateTask"); public AbstractTemplateExecutableTask(String stageName, @@ -44,9 +48,9 @@ public AbstractTemplateExecutableTask(String stageName, super(stageName, descriptor, action, executionMethod, rollbackMethod); } - protected void setConfigurationData(ChangeTemplate instance) { - Class parameterClass = instance.getConfigurationClass(); - Object data = descriptor.getConfiguration(); + protected void setConfigurationData(ChangeTemplate instance) { + Class parameterClass = instance.getConfigurationClass(); + CONFIG data = descriptor.getConfiguration(); if (data != null && Void.class != parameterClass) { instance.setConfiguration(FileUtil.getFromMap(parameterClass, data)); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index cd85c735e..8f5fc8f49 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -26,11 +26,17 @@ /** * Executable task for simple templates (single apply/rollback step). * Handles templates extending {@link AbstractSimpleTemplate}. + * + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type */ -public class SimpleTemplateExecutableTask extends AbstractTemplateExecutableTask> { +public class SimpleTemplateExecutableTask + extends AbstractTemplateExecutableTask> { public SimpleTemplateExecutableTask(String stageName, - SimpleTemplateLoadedChange descriptor, + SimpleTemplateLoadedChange descriptor, ChangeAction action, Method executionMethod, Method rollbackMethod) { @@ -38,12 +44,14 @@ public SimpleTemplateExecutableTask(String stageName, } @Override + @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - AbstractSimpleTemplate changeTemplateInstance = (AbstractSimpleTemplate) - executionRuntime.getInstance(descriptor.getConstructor()); + AbstractSimpleTemplate changeTemplateInstance = + (AbstractSimpleTemplate) + executionRuntime.getInstance(descriptor.getConstructor()); changeTemplateInstance.setTransactional(descriptor.isTransactional()); changeTemplateInstance.setChangeId(descriptor.getId()); @@ -56,10 +64,9 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) } } - @SuppressWarnings({"unchecked", "rawtypes"}) - protected void setTemplateData(AbstractSimpleTemplate instance) { - Object applyPayload = descriptor.getApplyPayload(); - Object rollbackPayload = descriptor.getRollbackPayload(); + protected void setTemplateData(AbstractSimpleTemplate instance) { + APPLY applyPayload = descriptor.getApplyPayload(); + ROLLBACK rollbackPayload = descriptor.getRollbackPayload(); if (applyPayload != null) { logger.debug("Setting payloads for simple template change[{}]", descriptor.getId()); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index b8fb01b6a..a86432fa9 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -28,11 +28,17 @@ /** * Executable task for steppable templates (multiple steps). * Handles templates extending {@link AbstractSteppableTemplate}. + * + * @param the configuration type for the template + * @param the apply payload type + * @param the rollback payload type */ -public class SteppableTemplateExecutableTask extends AbstractTemplateExecutableTask> { +public class SteppableTemplateExecutableTask + extends AbstractTemplateExecutableTask> { public SteppableTemplateExecutableTask(String stageName, - SteppableTemplateLoadedChange descriptor, + SteppableTemplateLoadedChange descriptor, ChangeAction action, Method executionMethod, Method rollbackMethod) { @@ -40,19 +46,21 @@ public SteppableTemplateExecutableTask(String stageName, } @Override + @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - AbstractSteppableTemplate instance = (AbstractSteppableTemplate) - executionRuntime.getInstance(descriptor.getConstructor()); + AbstractSteppableTemplate instance = + (AbstractSteppableTemplate) + executionRuntime.getInstance(descriptor.getConstructor()); instance.setTransactional(descriptor.isTransactional()); instance.setChangeId(descriptor.getId()); setConfigurationData(instance); - setStepsData(instance, descriptor.getSteps()); + setStepsData(instance); while (instance.advance()) { executionRuntime.executeMethodWithInjectedDependencies(instance, method); } @@ -64,13 +72,11 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) /** * Sets the steps data on the template instance. - * Uses a helper method to handle the generic type capture properly. + * Now with full type alignment through generic parameters. */ - @SuppressWarnings("unchecked") - private void setStepsData(AbstractSteppableTemplate instance, - List> steps) { - // Safe cast: the steps were created using the template's type information - instance.setSteps((List>) steps); + private void setStepsData(AbstractSteppableTemplate instance) { + List> steps = descriptor.getSteps(); + instance.setSteps(steps); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java index fe7126a1f..0facf711b 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/TemplateExecutableTaskBuilder.java @@ -97,7 +97,7 @@ public ExecutableTask build() { } rollbackMethod = null; } - return new SimpleTemplateExecutableTask( + return new SimpleTemplateExecutableTask<>( stageName, simple, changeAction, @@ -109,7 +109,7 @@ public ExecutableTask build() { if (rollbackMethod != null) { logger.trace("Change[{}] is a steppable template with rollback method", loadedTask.getId()); } - return new SteppableTemplateExecutableTask( + return new SteppableTemplateExecutableTask<>( stageName, steppable, changeAction, From 789089b873e409df4aa5705a8327a91c07073c1c Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 16:35:21 +0000 Subject: [PATCH 10/19] refactor: refactor typeSafety(4) --- .../template/AbstractSteppableTemplate.java | 21 ---------- .../flamingock/api/template/TemplateStep.java | 38 +++++++++---------- .../SteppableTemplateExecutableTask.java | 28 ++++++++------ .../loaded/AbstractTemplateLoadedChange.java | 1 + .../loaded/TemplateLoadedTaskBuilder.java | 4 +- ...teppableTemplateLoadedTaskBuilderTest.java | 10 ++--- 6 files changed, 43 insertions(+), 59 deletions(-) diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java index 553c842ce..e04581b57 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java @@ -49,8 +49,6 @@ * filter: {} * } * - *

The framework will automatically parse the steps from the YAML and inject - * them via {@link #setSteps}. * *

Rollback Behavior: *

    @@ -74,25 +72,6 @@ public AbstractSteppableTemplate(Class... additionalReflectiveClass) { super(additionalReflectiveClass); } - /** - * Sets the list of steps to execute. - * - * @param steps the list of template steps - */ - public final void setSteps(List> steps) { - this.steps = steps; - } - - public final boolean advance() { - if (atStep + 1 >= steps.size()) { - return false; - } - atStep++; - TemplateStep currentStep = steps.get(atStep); - this.setApplyPayload(currentStep.getApply()); - return true; - } - diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/TemplateStep.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/TemplateStep.java index 7ee3192f2..b4a999a6d 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/TemplateStep.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/TemplateStep.java @@ -56,15 +56,15 @@ */ public class TemplateStep { - private APPLY apply; - private ROLLBACK rollback; + private APPLY applyPayload; + private ROLLBACK rollbackPayload; public TemplateStep() { } - public TemplateStep(APPLY apply, ROLLBACK rollback) { - this.apply = apply; - this.rollback = rollback; + public TemplateStep(APPLY applyPayload, ROLLBACK rollbackPayload) { + this.applyPayload = applyPayload; + this.rollbackPayload = rollbackPayload; } /** @@ -72,17 +72,17 @@ public TemplateStep(APPLY apply, ROLLBACK rollback) { * * @return the apply payload (required) */ - public APPLY getApply() { - return apply; + public APPLY getApplyPayload() { + return applyPayload; } /** * Sets the apply payload for this step. * - * @param apply the apply payload + * @param applyPayload the apply payload */ - public void setApply(APPLY apply) { - this.apply = apply; + public void setApplyPayload(APPLY applyPayload) { + this.applyPayload = applyPayload; } /** @@ -90,17 +90,17 @@ public void setApply(APPLY apply) { * * @return the rollback payload, or null if no rollback is defined */ - public ROLLBACK getRollback() { - return rollback; + public ROLLBACK getRollbackPayload() { + return rollbackPayload; } /** * Sets the rollback payload for this step. * - * @param rollback the rollback payload (optional) + * @param rollbackPayload the rollback payload (optional) */ - public void setRollback(ROLLBACK rollback) { - this.rollback = rollback; + public void setRollbackPayload(ROLLBACK rollbackPayload) { + this.rollbackPayload = rollbackPayload; } /** @@ -109,15 +109,15 @@ public void setRollback(ROLLBACK rollback) { * @return true if a rollback payload is defined */ public boolean hasRollback() { - return rollback != null; + return rollbackPayload != null; } @Override public String toString() { StringBuilder sb = new StringBuilder("TemplateStep{"); - sb.append("apply=").append(apply); - if (rollback != null) { - sb.append(", rollback=").append(rollback); + sb.append("apply=").append(applyPayload); + if (rollbackPayload != null) { + sb.append(", rollback=").append(rollbackPayload); } sb.append('}'); return sb.toString(); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index a86432fa9..6771a43fb 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -48,11 +48,13 @@ public SteppableTemplateExecutableTask(String stageName, @Override @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { + + AbstractSteppableTemplate instance; try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - AbstractSteppableTemplate instance = + instance = (AbstractSteppableTemplate) executionRuntime.getInstance(descriptor.getConstructor()); @@ -60,23 +62,25 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) instance.setChangeId(descriptor.getId()); setConfigurationData(instance); - setStepsData(instance); - while (instance.advance()) { + } catch (Throwable ex) { + throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + } + + + int stepIndex = 0; + try { + List> steps = descriptor.getSteps(); + for(; stepIndex < steps.size() ; stepIndex++) { + TemplateStep currentSep = steps.get(stepIndex); + instance.setApplyPayload(currentSep.getApplyPayload()); executionRuntime.executeMethodWithInjectedDependencies(instance, method); } - } catch (Throwable ex) { + //TODO throw exception that holds the stepIndex; throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); } - } - /** - * Sets the steps data on the template instance. - * Now with full type alignment through generic parameters. - */ - private void setStepsData(AbstractSteppableTemplate instance) { - List> steps = descriptor.getSteps(); - instance.setSteps(steps); + } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java index d9e8d727b..59b692ee9 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/AbstractTemplateLoadedChange.java @@ -60,6 +60,7 @@ protected AbstractTemplateLoadedChange(String changeFileName, this.configuration = configuration; } + public CONFIG getConfiguration() { return configuration; } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index 60686e5e8..4bba5918b 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -282,12 +282,12 @@ private List> convertSteps(Constructor construct Object applyItemData = stepMap.get("apply"); if (applyItemData != null && Void.class != applyClass) { - step.setApply(FileUtil.getFromMap(applyClass, applyItemData)); + step.setApplyPayload(FileUtil.getFromMap(applyClass, applyItemData)); } Object rollbackItemData = stepMap.get("rollback"); if (rollbackItemData != null && Void.class != rollbackClass) { - step.setRollback(FileUtil.getFromMap(rollbackClass, rollbackItemData)); + step.setRollbackPayload(FileUtil.getFromMap(rollbackClass, rollbackItemData)); } result.add(step); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index 8986a759d..3308ffc89 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -228,9 +228,9 @@ void shouldBuildWithMultipleSteps() { assertNotNull(steps); assertEquals(3, steps.size()); // Verify steps are preserved in order - payloads are now typed objects - assertEquals("createCollection", steps.get(0).getApply()); - assertEquals("insertDocument", steps.get(1).getApply()); - assertEquals("createIndex", steps.get(2).getApply()); + assertEquals("createCollection", steps.get(0).getApplyPayload()); + assertEquals("insertDocument", steps.get(1).getApplyPayload()); + assertEquals("createIndex", steps.get(2).getApplyPayload()); } } @@ -289,8 +289,8 @@ void shouldBuildWithStepsHavingOnlyApply() { // Steps are now converted to List at load time List> steps = steppableResult.getSteps(); assertEquals(2, steps.size()); - assertNull(steps.get(0).getRollback()); - assertNull(steps.get(1).getRollback()); + assertNull(steps.get(0).getRollbackPayload()); + assertNull(steps.get(1).getRollbackPayload()); } } From 610e4404018c192b359a5b0935a14d40a66c87f9 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 16:54:41 +0000 Subject: [PATCH 11/19] refactor: refactor typeSafety(5) --- .../template/AbstractSteppableTemplate.java | 2 - .../core/error/ChangeExecutionException.java | 62 +++++++++---------- .../SteppableChangeExecutionException.java | 21 +++++++ .../executable/ReflectionExecutableTask.java | 2 +- .../SimpleTemplateExecutableTask.java | 2 +- .../SteppableTemplateExecutableTask.java | 13 ++-- 6 files changed, 60 insertions(+), 42 deletions(-) create mode 100644 core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java index e04581b57..d5c0b6cc2 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java @@ -65,8 +65,6 @@ public abstract class AbstractSteppableTemplate extends AbstractChangeTemplate { - private List> steps = new ArrayList<>(); - private int atStep = -1; public AbstractSteppableTemplate(Class... additionalReflectiveClass) { super(additionalReflectiveClass); diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/ChangeExecutionException.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/ChangeExecutionException.java index ddcbbd37d..02191eb2d 100644 --- a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/ChangeExecutionException.java +++ b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/ChangeExecutionException.java @@ -19,7 +19,7 @@ /** * Exception thrown when a Change execution fails. - * + * *

    This exception provides rich context about the failed change execution including: *

      *
    • Change ID and stage information
    • @@ -28,14 +28,14 @@ *
    • Target system information
    • *
    • Original cause with preserved stack trace
    • *
    - * - *

    This exception should be used instead of generic {@link FlamingockException} + * + *

    This exception should be used instead of generic {@link FlamingockException} * when change execution fails, as it provides much better debugging context. - * + * * @since 6.0.0 */ public class ChangeExecutionException extends FlamingockException { - + private final String changeId; private final String stageName; private final String executionMode; @@ -45,22 +45,22 @@ public class ChangeExecutionException extends FlamingockException { /** * Creates a new ChangeExecutionException with full execution context. * - * @param message descriptive error message - * @param changeId the ID of the failed change - * @param stageName the name of the stage containing the change - * @param executionMode the execution mode (e.g., "transactional", "non-transactional") + * @param message descriptive error message + * @param changeId the ID of the failed change + * @param stageName the name of the stage containing the change + * @param executionMode the execution mode (e.g., "transactional", "non-transactional") * @param executionDuration how long the change took before failing - * @param targetSystemId the target system where the change was being applied - * @param cause the underlying exception that caused the failure + * @param targetSystemId the target system where the change was being applied + * @param cause the underlying exception that caused the failure */ - public ChangeExecutionException(String message, - String changeId, - String stageName, - String executionMode, - Duration executionDuration, - String targetSystemId, - Throwable cause) { - super(buildContextualMessage(message, changeId, stageName, executionMode, executionDuration, targetSystemId), cause); + public ChangeExecutionException(String stageName, + String changeId, + String message, + String executionMode, + Duration executionDuration, + String targetSystemId, + Throwable cause) { + super(buildContextualMessage(stageName, changeId, message, executionMode, executionDuration, targetSystemId), cause); this.changeId = changeId; this.stageName = stageName; this.executionMode = executionMode; @@ -71,22 +71,22 @@ public ChangeExecutionException(String message, /** * Creates a new ChangeExecutionException with minimal context (for backward compatibility). * - * @param message descriptive error message * @param changeId the ID of the failed change - * @param cause the underlying exception that caused the failure + * @param message descriptive error message + * @param cause the underlying exception that caused the failure */ - public ChangeExecutionException(String message, String changeId, Throwable cause) { - this(message, changeId, null, null, null, null, cause); + public ChangeExecutionException(String changeId, String message, Throwable cause) { + this(null, changeId, message, null, null, null, cause); } - private static String buildContextualMessage(String message, - String changeId, - String stageName, - String executionMode, - Duration executionDuration, - String targetSystemId) { + private static String buildContextualMessage(String stageName, + String changeId, + String message, + String executionMode, + Duration executionDuration, + String targetSystemId) { StringBuilder contextMessage = new StringBuilder(message); - + if (changeId != null) { contextMessage.append("\n Change ID: ").append(changeId); } @@ -102,7 +102,7 @@ private static String buildContextualMessage(String message, if (targetSystemId != null) { contextMessage.append("\n Target System: ").append(targetSystemId); } - + return contextMessage.toString(); } diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java new file mode 100644 index 000000000..ff5e19812 --- /dev/null +++ b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java @@ -0,0 +1,21 @@ +package io.flamingock.internal.common.core.error; + +import java.time.Duration; + +public class SteppableChangeExecutionException extends ChangeExecutionException { + private final int step; + + public SteppableChangeExecutionException(String stageName, String changeId, int step, String message, String executionMode, Duration executionDuration, String targetSystemId, Throwable cause) { + super(stageName, changeId, message, executionMode, executionDuration, targetSystemId, cause); + this.step = step; + } + + public SteppableChangeExecutionException(String changeId, int step, String message, Throwable cause) { + super(changeId, message, cause); + this.step = step; + } + + public int getStep() { + return step; + } +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java index 6aaf6bcd6..9a893a102 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java @@ -79,7 +79,7 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method try { executionRuntime.executeMethodWithInjectedDependencies(instance, method); } catch (Throwable ex) { - throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 8f5fc8f49..70134e489 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -60,7 +60,7 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); } catch (Throwable ex) { - throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index 6771a43fb..cc18476c8 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -15,9 +15,11 @@ */ package io.flamingock.internal.core.task.executable; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; +import io.flamingock.internal.common.core.error.SteppableChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; @@ -49,13 +51,12 @@ public SteppableTemplateExecutableTask(String stageName, @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { - AbstractSteppableTemplate instance; + AbstractChangeTemplate instance; try { logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - instance = - (AbstractSteppableTemplate) + instance = (AbstractChangeTemplate) executionRuntime.getInstance(descriptor.getConstructor()); instance.setTransactional(descriptor.isTransactional()); @@ -63,10 +64,9 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) setConfigurationData(instance); } catch (Throwable ex) { - throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } - int stepIndex = 0; try { List> steps = descriptor.getSteps(); @@ -76,8 +76,7 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method) executionRuntime.executeMethodWithInjectedDependencies(instance, method); } } catch (Throwable ex) { - //TODO throw exception that holds the stepIndex; - throw new ChangeExecutionException(ex.getMessage(), this.getId(), ex); + throw new SteppableChangeExecutionException(this.getId(), stepIndex, ex.getMessage(), ex); } From f1fe88c15da3b28b4826f7f216a43fd09ac26b32 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 18:06:17 +0000 Subject: [PATCH 12/19] refactor: refactor typeSafety(6) --- .../internal/core/task/executable/ExecutableTask.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java index a8475f6c0..445261372 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java @@ -35,8 +35,6 @@ public interface ExecutableTask extends TaskDescriptor { ChangeAction getAction(); - void addRollback(Rollback rollback); - List getRollbackChain(); } \ No newline at end of file From 8f9969ad68949644e1141cc6317b390571d6f9a1 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 18:06:45 +0000 Subject: [PATCH 13/19] refactor: refactor typeSafety(7) --- .../core/task/executable/ReflectionExecutableTask.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java index 9a893a102..1a103e750 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java @@ -57,11 +57,6 @@ public ReflectionExecutableTask(String stageName, } } - @Override - public void addRollback(Rollback rollback) { - rollbackChain.add(rollback); - - } @Override public List getRollbackChain() { From a870ae616b8ef4cf789926da771cef4436df23e0 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 18:11:08 +0000 Subject: [PATCH 14/19] refactor: added rollBack to ReflectionExecutable --- .../internal/core/task/executable/ExecutableTask.java | 6 +++++- .../core/task/executable/ReflectionExecutableTask.java | 9 ++++++++- .../core/task/navigation/step/ExecutableStep.java | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java index 445261372..ebbbe74c6 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java @@ -27,7 +27,10 @@ public interface ExecutableTask extends TaskDescriptor { String getStageName(); - void execute(ExecutionRuntime runtimeHelper); + void apply(ExecutionRuntime executionRuntime); + + + void rollback(ExecutionRuntime executionRuntime); String getExecutionMethodName(); @@ -35,6 +38,7 @@ public interface ExecutableTask extends TaskDescriptor { ChangeAction getAction(); + @Deprecated List getRollbackChain(); } \ No newline at end of file diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java index 1a103e750..96f9934dd 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java @@ -40,7 +40,9 @@ public class ReflectionExecutableTask extends AbstractExecutableTask implements ExecutableTask { protected final Method executionMethod; + protected final Method rollbackMethod; + @Deprecated protected final List rollbackChain; @@ -51,6 +53,7 @@ public ReflectionExecutableTask(String stageName, Method rollbackMethod) { super(stageName, descriptor, action); this.executionMethod = executionMethod; + this.rollbackMethod = rollbackMethod; rollbackChain = new LinkedList<>(); if(rollbackMethod != null) { rollbackChain.add(buildRollBack(rollbackMethod)); @@ -64,9 +67,13 @@ public List getRollbackChain() { } @Override - public void execute(ExecutionRuntime executionRuntime) { + public void apply(ExecutionRuntime executionRuntime) { executeInternal(executionRuntime, executionMethod); + } + @Override + public void rollback(ExecutionRuntime executionRuntime) { + executeInternal(executionRuntime, rollbackMethod); } protected void executeInternal(ExecutionRuntime executionRuntime, Method method ) { diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/ExecutableStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/ExecutableStep.java index 12eb0f283..794303dd8 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/ExecutableStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/ExecutableStep.java @@ -30,7 +30,7 @@ public ExecutableStep(StartStep step) { public ExecutionStep execute(ExecutionRuntime executionRuntime) { StopWatch stopWatch = StopWatch.startAndGet(); try { - task.execute(executionRuntime); + task.apply(executionRuntime); return SuccessApplyStep.instance(this, stopWatch.getElapsed()); } catch (Throwable throwable) { return FailedExecutionStep.instance(this, stopWatch.getElapsed(), throwable); From 765917f84d6748a970565a1926f30983c3830a3d Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 18:56:01 +0000 Subject: [PATCH 15/19] refactor: removed rollbackChain --- .../store/audit/domain/RuntimeContext.java | 6 +++--- .../core/task/executable/ExecutableTask.java | 3 ++- .../executable/ReflectionExecutableTask.java | 16 +++++++--------- .../strategy/NonTxChangeProcessStrategy.java | 10 +++++----- .../SharedTxChangeProcessStrategy.java | 12 +++++------- .../SimpleTxChangeProcessStrategy.java | 11 ----------- .../navigation/step/RollableFailedStep.java | 7 ++++--- .../FailedAfterExecutionAuditStep.java | 7 ++----- .../step/afteraudit/RollableStep.java | 16 ++++++++-------- .../failed/CompleteAutoRolledBackStep.java | 8 ++------ .../rolledback/FailedManualRolledBackStep.java | 5 +++-- .../step/rolledback/ManualRolledBackStep.java | 18 +++++++----------- 12 files changed, 48 insertions(+), 71 deletions(-) diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/store/audit/domain/RuntimeContext.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/store/audit/domain/RuntimeContext.java index a3308b891..95f83f2c2 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/store/audit/domain/RuntimeContext.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/external/store/audit/domain/RuntimeContext.java @@ -106,7 +106,7 @@ private Builder() { public Builder setStartStep(StartStep taskStep) { duration = 0L; - methodExecutor = taskStep.getTask().getExecutionMethodName(); + methodExecutor = taskStep.getTask().getApplyMethodName(); stageName = taskStep.getTask().getStageName(); setResult(taskStep); return this; @@ -114,7 +114,7 @@ public Builder setStartStep(StartStep taskStep) { public Builder setExecutionStep(ExecutionStep taskStep) { duration = taskStep.getDuration(); - methodExecutor = taskStep.getTask().getExecutionMethodName(); + methodExecutor = taskStep.getTask().getApplyMethodName(); stageName = taskStep.getTask().getStageName(); setResult(taskStep); return this; @@ -122,7 +122,7 @@ public Builder setExecutionStep(ExecutionStep taskStep) { public Builder setManualRollbackStep(ManualRolledBackStep rolledBackStep) { duration = rolledBackStep.getDuration(); - methodExecutor = rolledBackStep.getRollback().getRollbackMethodName(); + methodExecutor = rolledBackStep.getTask().getRollbackMethodName(); stageName = rolledBackStep.getTask().getStageName(); setResult(rolledBackStep); return this; diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java index ebbbe74c6..cc4f3a3bb 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java @@ -29,10 +29,11 @@ public interface ExecutableTask extends TaskDescriptor { void apply(ExecutionRuntime executionRuntime); + String getApplyMethodName(); void rollback(ExecutionRuntime executionRuntime); - String getExecutionMethodName(); + String getRollbackMethodName(); boolean isAlreadyApplied(); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java index 96f9934dd..e0765d21d 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java @@ -42,9 +42,6 @@ public class ReflectionExecutableTask rollbackChain; - public ReflectionExecutableTask(String stageName, REFLECTION_TASK_DESCRIPTOR descriptor, @@ -54,16 +51,12 @@ public ReflectionExecutableTask(String stageName, super(stageName, descriptor, action); this.executionMethod = executionMethod; this.rollbackMethod = rollbackMethod; - rollbackChain = new LinkedList<>(); - if(rollbackMethod != null) { - rollbackChain.add(buildRollBack(rollbackMethod)); - } } @Override public List getRollbackChain() { - return rollbackChain; + return null; } @Override @@ -86,10 +79,15 @@ protected void executeInternal(ExecutionRuntime executionRuntime, Method method } @Override - public String getExecutionMethodName() { + public String getApplyMethodName() { return executionMethod.getName(); } + @Override + public String getRollbackMethodName() { + return rollbackMethod.getName(); + } + private Rollback buildRollBack(Method rollbackMethod) { return new Rollback() { @Override diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/NonTxChangeProcessStrategy.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/NonTxChangeProcessStrategy.java index a22fa4456..de903961e 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/NonTxChangeProcessStrategy.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/NonTxChangeProcessStrategy.java @@ -29,6 +29,7 @@ import io.flamingock.internal.core.task.navigation.step.StartStep; import io.flamingock.internal.core.task.navigation.step.afteraudit.AfterExecutionAuditStep; import io.flamingock.internal.core.task.navigation.step.afteraudit.FailedAfterExecutionAuditStep; +import io.flamingock.internal.core.task.navigation.step.afteraudit.RollableStep; import io.flamingock.internal.core.task.navigation.step.execution.ExecutionStep; import io.flamingock.internal.core.task.navigation.step.rolledback.ManualRolledBackStep; import io.flamingock.internal.util.TimeService; @@ -118,10 +119,9 @@ protected ChangeProcessResult doApplyChange() { } private void rollbackActualChangeAndChain(FailedAfterExecutionAuditStep rollableFailedStep, ExecutionContext executionContext) { - rollableFailedStep.getRollbackSteps().forEach(rollableStep -> { - ManualRolledBackStep rolledBack = targetSystemOps.rollbackChange(rollableStep::rollback, buildExecutionRuntime()); - stepLogger.logManualRollbackResult(rolledBack); - auditAndLogManualRollback(rolledBack, executionContext); - }); + RollableStep rollableStep = rollableFailedStep.getRollbackStep(); + ManualRolledBackStep rolledBack = targetSystemOps.rollbackChange(rollableStep::rollback, buildExecutionRuntime()); + stepLogger.logManualRollbackResult(rolledBack); + auditAndLogManualRollback(rolledBack, executionContext); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SharedTxChangeProcessStrategy.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SharedTxChangeProcessStrategy.java index 0465203e5..602b3a795 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SharedTxChangeProcessStrategy.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SharedTxChangeProcessStrategy.java @@ -30,6 +30,7 @@ import io.flamingock.internal.core.task.navigation.step.StartStep; import io.flamingock.internal.core.task.navigation.step.afteraudit.AfterExecutionAuditStep; import io.flamingock.internal.core.task.navigation.step.afteraudit.FailedAfterExecutionAuditStep; +import io.flamingock.internal.core.task.navigation.step.afteraudit.RollableStep; import io.flamingock.internal.core.task.navigation.step.complete.failed.CompleteAutoRolledBackStep; import io.flamingock.internal.core.task.navigation.step.execution.ExecutionStep; import io.flamingock.internal.core.task.navigation.step.rolledback.ManualRolledBackStep; @@ -148,13 +149,10 @@ private void auditIfExecutionFailure(Wrapper executionStep) { private void rollbackChain(RollableFailedStep rollableFailedStep, ExecutionContext executionContext) { // Skip first rollback (main change) as transaction already rolled it back - rollableFailedStep.getRollbackSteps() - .stream().skip(1) - .forEach(rollableStep -> { - ManualRolledBackStep rolledBack = targetSystemOps.rollbackChange(rollableStep::rollback, buildExecutionRuntime()); - stepLogger.logManualRollbackResult(rolledBack); - auditAndLogManualRollback(rolledBack, executionContext); - }); + RollableStep rollableStep = rollableFailedStep.getRollbackStep(); + ManualRolledBackStep rolledBack = targetSystemOps.rollbackChange(rollableStep::rollback, buildExecutionRuntime()); + stepLogger.logManualRollbackResult(rolledBack); + auditAndLogManualRollback(rolledBack, executionContext); } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SimpleTxChangeProcessStrategy.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SimpleTxChangeProcessStrategy.java index f82b07b69..ad7e3becc 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SimpleTxChangeProcessStrategy.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/navigator/strategy/SimpleTxChangeProcessStrategy.java @@ -138,7 +138,6 @@ protected ChangeProcessResult doApplyChange() { Throwable mainError = ((FailedAfterExecutionAuditStep)afterAudit) .getMainError(); auditAndLogAutoRollback(); - rollbackChain((RollableFailedStep) afterAudit, executionContext); ChangeResult result = resultBuilder .rolledBack() .error(mainError) @@ -147,16 +146,6 @@ protected ChangeProcessResult doApplyChange() { } } - private void rollbackChain(RollableFailedStep rollableFailedStep, ExecutionContext executionContext) { - // Skip first rollback (main change) as transaction already rolled it back - rollableFailedStep.getRollbackSteps() - .stream().skip(1) - .forEach(rollableStep -> { - ManualRolledBackStep rolledBack = targetSystemOps.rollbackChange(rollableStep::rollback, buildExecutionRuntime()); - stepLogger.logManualRollbackResult(rolledBack); - auditAndLogManualRollback(rolledBack, executionContext); - }); - } protected void auditAndLogAutoRollback() { diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/RollableFailedStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/RollableFailedStep.java index 914676da2..6d1a8c5c0 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/RollableFailedStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/RollableFailedStep.java @@ -17,8 +17,9 @@ import io.flamingock.internal.core.task.navigation.step.afteraudit.RollableStep; -import java.util.List; - public interface RollableFailedStep extends FailedStep { - List getRollbackSteps(); + + RollableStep getRollbackStep(); + + } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/FailedAfterExecutionAuditStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/FailedAfterExecutionAuditStep.java index 7e986c853..a21e8031e 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/FailedAfterExecutionAuditStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/FailedAfterExecutionAuditStep.java @@ -46,11 +46,8 @@ protected FailedAfterExecutionAuditStep(ExecutableTask task, boolean successAudi } @Override - public final List getRollbackSteps() { - return task.getRollbackChain() - .stream() - .map(RollableStep::new) - .collect(Collectors.toList()); + public final RollableStep getRollbackStep() { + return new RollableStep(getTask()); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/RollableStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/RollableStep.java index 7d3922a7b..6b1e52938 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/RollableStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/afteraudit/RollableStep.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.navigation.step.afteraudit; +import io.flamingock.internal.core.task.executable.ExecutableTask; import io.flamingock.internal.core.task.navigation.step.AbstractTaskStep; import io.flamingock.internal.core.task.navigation.step.rolledback.ManualRolledBackStep; import io.flamingock.internal.core.runtime.ExecutionRuntime; @@ -22,21 +23,20 @@ import io.flamingock.internal.util.StopWatch; public final class RollableStep extends AbstractTaskStep { - private final Rollback rollback; - public RollableStep(Rollback rollback) { - super(rollback.getTask()); - this.rollback = rollback; + public RollableStep(ExecutableTask executableChange) { + super(executableChange); } - public ManualRolledBackStep rollback(ExecutionRuntime runtimeHelper) { + public ManualRolledBackStep rollback(ExecutionRuntime executionRuntime) { StopWatch stopWatch = StopWatch.startAndGet(); + ExecutableTask change = this.getTask(); try { - rollback.rollback(runtimeHelper); - return ManualRolledBackStep.successfulRollback(rollback, stopWatch.getElapsed()); + change.rollback(executionRuntime); + return ManualRolledBackStep.successfulRollback(change, stopWatch.getElapsed()); } catch (Throwable throwable) { - return ManualRolledBackStep.failedRollback(rollback, stopWatch.getElapsed(), throwable); + return ManualRolledBackStep.failedRollback(change, stopWatch.getElapsed(), throwable); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/complete/failed/CompleteAutoRolledBackStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/complete/failed/CompleteAutoRolledBackStep.java index 6b09c24e8..895a324a5 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/complete/failed/CompleteAutoRolledBackStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/complete/failed/CompleteAutoRolledBackStep.java @@ -31,11 +31,7 @@ public CompleteAutoRolledBackStep(ExecutableTask task, boolean rollbackSuccess) @Override - public List getRollbackSteps() { - return task.getRollbackChain() - .stream() - .skip(1)//Skips the first one(its own rollback), because it's AutoRollback - .map(RollableStep::new) - .collect(Collectors.toList()); + public final RollableStep getRollbackStep() { + return new RollableStep(getTask()); } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/FailedManualRolledBackStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/FailedManualRolledBackStep.java index 865f1d7e2..adf6925cd 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/FailedManualRolledBackStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/FailedManualRolledBackStep.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.navigation.step.rolledback; +import io.flamingock.internal.core.task.executable.ExecutableTask; import io.flamingock.internal.core.task.navigation.step.FailedWithErrorStep; import io.flamingock.internal.core.task.executable.Rollback; @@ -23,8 +24,8 @@ public final class FailedManualRolledBackStep extends ManualRolledBackStep imple private final Throwable error; - FailedManualRolledBackStep(Rollback rollback, long duration, Throwable error) { - super(rollback, false, duration); + FailedManualRolledBackStep(ExecutableTask executableChange, long duration, Throwable error) { + super(executableChange, false, duration); this.error = error; } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/ManualRolledBackStep.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/ManualRolledBackStep.java index 0f00111ee..a606eb72a 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/ManualRolledBackStep.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/navigation/step/rolledback/ManualRolledBackStep.java @@ -15,6 +15,7 @@ */ package io.flamingock.internal.core.task.navigation.step.rolledback; +import io.flamingock.internal.core.task.executable.ExecutableTask; import io.flamingock.internal.core.task.navigation.step.FailedStep; import io.flamingock.internal.core.task.navigation.step.SuccessableStep; import io.flamingock.internal.core.task.navigation.step.complete.failed.CompletedFailedManualRollback; @@ -24,20 +25,18 @@ public class ManualRolledBackStep extends RolledBackStep implements SuccessableStep, FailedStep { private final long duration; - private final Rollback rollback; - protected ManualRolledBackStep(Rollback rollback, boolean rollbackSuccess, long duration) { - super(rollback.getTask(), rollbackSuccess); - this.rollback = rollback; + protected ManualRolledBackStep(ExecutableTask executableChange, boolean rollbackSuccess, long duration) { + super(executableChange, rollbackSuccess); this.duration = duration; } - public static ManualRolledBackStep successfulRollback(Rollback rollback, long duration) { - return new ManualRolledBackStep(rollback, true, duration); + public static ManualRolledBackStep successfulRollback(ExecutableTask executableChange, long duration) { + return new ManualRolledBackStep(executableChange, true, duration); } - public static ManualRolledBackStep failedRollback(Rollback rollback, long duration, Throwable error) { - return new FailedManualRolledBackStep(rollback, duration, error); + public static ManualRolledBackStep failedRollback(ExecutableTask executableChange, long duration, Throwable error) { + return new FailedManualRolledBackStep(executableChange, duration, error); } public CompletedFailedManualRollback applyAuditResult(Result auditResult) { @@ -48,7 +47,4 @@ public long getDuration() { return duration; } - public Rollback getRollback() { - return rollback; - } } From d23e90a1e41ca6161cac75b2ec5173499f24cf5e Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 20:43:55 +0000 Subject: [PATCH 16/19] refactor: granular rollback in steppable --- .../SteppableChangeExecutionException.java | 21 ------ .../task/executable/CodeExecutableTask.java | 73 +++++++++++++++++++ .../core/task/executable/ExecutableTask.java | 3 - .../executable/ReflectionExecutableTask.java | 46 +----------- .../SimpleTemplateExecutableTask.java | 41 +++++------ .../SteppableTemplateExecutableTask.java | 61 +++++++++++----- .../builder/CodeExecutableTaskBuilder.java | 15 ++-- 7 files changed, 145 insertions(+), 115 deletions(-) delete mode 100644 core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java create mode 100644 core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/CodeExecutableTask.java diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java deleted file mode 100644 index ff5e19812..000000000 --- a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/error/SteppableChangeExecutionException.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.flamingock.internal.common.core.error; - -import java.time.Duration; - -public class SteppableChangeExecutionException extends ChangeExecutionException { - private final int step; - - public SteppableChangeExecutionException(String stageName, String changeId, int step, String message, String executionMode, Duration executionDuration, String targetSystemId, Throwable cause) { - super(stageName, changeId, message, executionMode, executionDuration, targetSystemId, cause); - this.step = step; - } - - public SteppableChangeExecutionException(String changeId, int step, String message, Throwable cause) { - super(changeId, message, cause); - this.step = step; - } - - public int getStep() { - return step; - } -} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/CodeExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/CodeExecutableTask.java new file mode 100644 index 000000000..23ea02912 --- /dev/null +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/CodeExecutableTask.java @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.executable; + +import io.flamingock.internal.common.core.error.ChangeExecutionException; +import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.runtime.ExecutionRuntime; +import io.flamingock.internal.core.task.loaded.AbstractReflectionLoadedTask; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * This class is a reflection version of the ExecutableTask. + *

    + * It creates a new instance on demand in every execution(apply and rollback), because it's intended to be applied + * just once. The only case it will be potentially applied twice is if it fails, and in that case will only happen + * once(in case of sequential execution) or very few times(in case or parallel execution and happen to fail multiple + * concurrent tasks at the same time),because after that the process will abort. + *

    + * For this reason it's more optimal to do it on demand, that articulate some synchronisation mechanism. + *

    + * However, the methods are extracted in advance, so we can spot wrong configuration before starting the process and + * fail fast. + */ +public class CodeExecutableTask + extends ReflectionExecutableTask { + + + public CodeExecutableTask(String stageName, + REFLECTION_TASK_DESCRIPTOR descriptor, + ChangeAction action, + Method executionMethod, + Method rollbackMethod) { + super(stageName, descriptor, action, executionMethod, rollbackMethod); + } + + + + @Override + public void apply(ExecutionRuntime executionRuntime) { + executeInternal(executionRuntime, executionMethod); + } + + @Override + public void rollback(ExecutionRuntime executionRuntime) { + executeInternal(executionRuntime, rollbackMethod); + } + + protected void executeInternal(ExecutionRuntime executionRuntime, Method method ) { + Object instance = executionRuntime.getInstance(descriptor.getConstructor()); + try { + executionRuntime.executeMethodWithInjectedDependencies(instance, method); + } catch (Throwable ex) { + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); + } + } + + +} diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java index cc4f3a3bb..62f415633 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ExecutableTask.java @@ -39,7 +39,4 @@ public interface ExecutableTask extends TaskDescriptor { ChangeAction getAction(); - @Deprecated - List getRollbackChain(); - } \ No newline at end of file diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java index e0765d21d..1de857237 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/ReflectionExecutableTask.java @@ -37,7 +37,8 @@ * However, the methods are extracted in advance, so we can spot wrong configuration before starting the process and * fail fast. */ -public class ReflectionExecutableTask extends AbstractExecutableTask implements ExecutableTask { +public abstract class ReflectionExecutableTask + extends AbstractExecutableTask implements ExecutableTask { protected final Method executionMethod; protected final Method rollbackMethod; @@ -53,31 +54,6 @@ public ReflectionExecutableTask(String stageName, this.rollbackMethod = rollbackMethod; } - - @Override - public List getRollbackChain() { - return null; - } - - @Override - public void apply(ExecutionRuntime executionRuntime) { - executeInternal(executionRuntime, executionMethod); - } - - @Override - public void rollback(ExecutionRuntime executionRuntime) { - executeInternal(executionRuntime, rollbackMethod); - } - - protected void executeInternal(ExecutionRuntime executionRuntime, Method method ) { - Object instance = executionRuntime.getInstance(descriptor.getConstructor()); - try { - executionRuntime.executeMethodWithInjectedDependencies(instance, method); - } catch (Throwable ex) { - throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); - } - } - @Override public String getApplyMethodName() { return executionMethod.getName(); @@ -88,23 +64,5 @@ public String getRollbackMethodName() { return rollbackMethod.getName(); } - private Rollback buildRollBack(Method rollbackMethod) { - return new Rollback() { - @Override - public ExecutableTask getTask() { - return ReflectionExecutableTask.this; - } - - @Override - public void rollback(ExecutionRuntime executionRuntime) { - executeInternal(executionRuntime, rollbackMethod); - } - - @Override - public String getRollbackMethodName() { - return rollbackMethod.getName(); - } - }; - } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 70134e489..2bea0c909 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -44,37 +44,36 @@ public SimpleTemplateExecutableTask(String stageName, } @Override + public void apply(ExecutionRuntime executionRuntime) { + logger.debug("Applying change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); + logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); + executeInternal(executionRuntime, executionMethod); + } + + @Override + public void rollback(ExecutionRuntime executionRuntime) { + logger.debug("Rolling back change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); + executeInternal(executionRuntime, rollbackMethod); + } + @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { try { - logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); - logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - AbstractSimpleTemplate changeTemplateInstance = - (AbstractSimpleTemplate) + AbstractSimpleTemplate instance = (AbstractSimpleTemplate) executionRuntime.getInstance(descriptor.getConstructor()); - changeTemplateInstance.setTransactional(descriptor.isTransactional()); - changeTemplateInstance.setChangeId(descriptor.getId()); - setConfigurationData(changeTemplateInstance); - setTemplateData(changeTemplateInstance); + instance.setTransactional(descriptor.isTransactional()); + instance.setChangeId(descriptor.getId()); + logger.trace("Setting payloads for simple template change[{}]", descriptor.getId()); + setConfigurationData(instance); + instance.setApplyPayload(descriptor.getApplyPayload()); + instance.setRollbackPayload(descriptor.getRollbackPayload()); - executionRuntime.executeMethodWithInjectedDependencies(changeTemplateInstance, method); + executionRuntime.executeMethodWithInjectedDependencies(instance, method); } catch (Throwable ex) { throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } } - protected void setTemplateData(AbstractSimpleTemplate instance) { - APPLY applyPayload = descriptor.getApplyPayload(); - ROLLBACK rollbackPayload = descriptor.getRollbackPayload(); - - if (applyPayload != null) { - logger.debug("Setting payloads for simple template change[{}]", descriptor.getId()); - instance.setApplyPayload(applyPayload); - instance.setRollbackPayload(rollbackPayload); - } else { - logger.warn("No apply payload provided for simple template-based change[{}]", descriptor.getId()); - } - } } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index cc18476c8..a0a3dd634 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -19,10 +19,10 @@ import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; -import io.flamingock.internal.common.core.error.SteppableChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Method; import java.util.List; @@ -37,7 +37,9 @@ */ public class SteppableTemplateExecutableTask extends AbstractTemplateExecutableTask> { + SteppableTemplateLoadedChange> { + + private int stepIndex = -1; public SteppableTemplateExecutableTask(String stageName, SteppableTemplateLoadedChange descriptor, @@ -48,38 +50,59 @@ public SteppableTemplateExecutableTask(String stageName, } @Override - @SuppressWarnings("unchecked") - protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { + public void apply(ExecutionRuntime executionRuntime) { + AbstractChangeTemplate instance = buildInstance(executionRuntime); - AbstractChangeTemplate instance; try { - logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); - logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); - - instance = (AbstractChangeTemplate) - executionRuntime.getInstance(descriptor.getConstructor()); - - instance.setTransactional(descriptor.isTransactional()); - instance.setChangeId(descriptor.getId()); - setConfigurationData(instance); - + List> steps = descriptor.getSteps(); + while (stepIndex >= -1 && stepIndex + 1 < steps.size()) { + TemplateStep currentSep = steps.get(stepIndex + 1); + instance.setApplyPayload(currentSep.getApplyPayload()); + stepIndex++; + executionRuntime.executeMethodWithInjectedDependencies(instance, executionMethod); + } } catch (Throwable ex) { throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } + } + + @Override + public void rollback(ExecutionRuntime executionRuntime) { + AbstractChangeTemplate instance = buildInstance(executionRuntime); - int stepIndex = 0; try { List> steps = descriptor.getSteps(); - for(; stepIndex < steps.size() ; stepIndex++) { + while (stepIndex >= 0 && stepIndex < steps.size()) { TemplateStep currentSep = steps.get(stepIndex); instance.setApplyPayload(currentSep.getApplyPayload()); - executionRuntime.executeMethodWithInjectedDependencies(instance, method); + executionRuntime.executeMethodWithInjectedDependencies(instance, rollbackMethod); + stepIndex--; } } catch (Throwable ex) { - throw new SteppableChangeExecutionException(this.getId(), stepIndex, ex.getMessage(), ex); + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); } + } + @NotNull + @SuppressWarnings("unchecked") + private AbstractChangeTemplate buildInstance(ExecutionRuntime executionRuntime) { + AbstractChangeTemplate instance; + try { + logger.debug("Starting execution of change[{}] with template: {}", descriptor.getId(), descriptor.getTemplateClass()); + logger.debug("change[{}] transactional: {}", descriptor.getId(), descriptor.isTransactional()); + + instance = (AbstractChangeTemplate) + executionRuntime.getInstance(descriptor.getConstructor()); + instance.setTransactional(descriptor.isTransactional()); + instance.setChangeId(descriptor.getId()); + setConfigurationData(instance); + + } catch (Throwable ex) { + throw new ChangeExecutionException(this.getId(), ex.getMessage(), ex); + } + return instance; } + } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/CodeExecutableTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/CodeExecutableTaskBuilder.java index e3915c669..72bb32440 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/CodeExecutableTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/builder/CodeExecutableTaskBuilder.java @@ -16,11 +16,12 @@ package io.flamingock.internal.core.task.executable.builder; import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.task.executable.CodeExecutableTask; import io.flamingock.internal.core.task.executable.ExecutableTask; import io.flamingock.internal.core.task.executable.ReflectionExecutableTask; import io.flamingock.internal.core.task.loaded.AbstractLoadedTask; -import io.flamingock.internal.core.task.loaded.CodeLoadedChange; import io.flamingock.internal.core.task.loaded.AbstractReflectionLoadedTask; +import io.flamingock.internal.core.task.loaded.CodeLoadedChange; import java.lang.reflect.Method; import java.util.Optional; @@ -47,7 +48,7 @@ public static boolean supports(AbstractLoadedTask loadedTask) { @Override public CodeLoadedChange cast(AbstractLoadedTask loadedTask) { - return (CodeLoadedChange)loadedTask; + return (CodeLoadedChange) loadedTask; } @Override @@ -77,18 +78,18 @@ public ExecutableTask build() { * New ChangeAction-based method for building tasks. */ private ReflectionExecutableTask getTasksFromReflection(String stageName, - CodeLoadedChange loadedTask, - ChangeAction action) { + CodeLoadedChange loadedTask, + ChangeAction action) { return buildTasksInternal(stageName, loadedTask, action); } private ReflectionExecutableTask buildTasksInternal(String stageName, - CodeLoadedChange loadedTask, - ChangeAction action) { + CodeLoadedChange loadedTask, + ChangeAction action) { Method executionMethod = loadedTask.getApplyMethod(); Optional rollbackMethodOpt = loadedTask.getRollbackMethod(); - return new ReflectionExecutableTask<>( + return new CodeExecutableTask<>( stageName, loadedTask, action, From afe9191ad133892c7a4de682ef8d3851376f5878 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 21:35:16 +0000 Subject: [PATCH 17/19] test: steppeable --- CLAUDE.md | 19 +- .../SteppableTemplateExecutableTask.java | 9 +- .../SteppableTemplateExecutableTaskTest.java | 478 ++++++++++++++++++ 3 files changed, 503 insertions(+), 3 deletions(-) create mode 100644 core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java diff --git a/CLAUDE.md b/CLAUDE.md index 1994e26e9..17d89a54e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -461,7 +461,24 @@ TemplateExecutableTask (abstract base) **Note:** Preview phase (`TemplatePreviewChange`) remains unified since YAML is parsed before template type is known. ->>>>>>> Stashed changes +### SteppableTemplateExecutableTask Apply/Rollback Lifecycle + +The `SteppableTemplateExecutableTask` manages multi-step execution with per-step rollback: + +**Apply Phase:** +- Iterates through steps in order (0 → N-1) +- Sets `applyPayload` on template instance before executing `@Apply` method +- `stepIndex` tracks current progress (-1 before start, N-1 after all steps complete) +- On failure at step N, `stepIndex = N` (points to failed step) + +**Rollback Phase (on apply failure):** +- Rolls back from current `stepIndex` down to 0 (reverse order) +- Sets `rollbackPayload` on template instance before executing `@Rollback` method +- **Skips steps without rollback payload** (`hasRollback()` returns false) +- **Skips if template has no `@Rollback` method** (logs warning) + +**Key Design Decision:** Same `SteppableTemplateExecutableTask` instance is used for both apply and rollback (no retry). The `stepIndex` state persists to enable rollback from the exact failure point. + ### Dependency Injection in Templates Template methods (`@Apply`, `@Rollback`) receive dependencies as **method parameters**, not constructor injection: diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index a0a3dd634..e218100eb 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -74,8 +74,13 @@ public void rollback(ExecutionRuntime executionRuntime) { List> steps = descriptor.getSteps(); while (stepIndex >= 0 && stepIndex < steps.size()) { TemplateStep currentSep = steps.get(stepIndex); - instance.setApplyPayload(currentSep.getApplyPayload()); - executionRuntime.executeMethodWithInjectedDependencies(instance, rollbackMethod); + if(currentSep.hasRollback() && rollbackMethod != null) { + instance.setRollbackPayload(currentSep.getRollbackPayload()); + executionRuntime.executeMethodWithInjectedDependencies(instance, rollbackMethod); + } else { + logger.warn("Skipping rollback for change[{}], step[{}] -> payload provided[{}], rollback support in template[{}]", + getId(), stepIndex, currentSep.hasRollback(), rollbackMethod != null); + } stepIndex--; } } catch (Throwable ex) { diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java new file mode 100644 index 000000000..fadc377e4 --- /dev/null +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java @@ -0,0 +1,478 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.internal.core.task.executable; + +import io.flamingock.api.annotations.Apply; +import io.flamingock.api.annotations.Rollback; +import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.template.TemplateStep; +import io.flamingock.internal.common.core.error.ChangeExecutionException; +import io.flamingock.internal.common.core.error.FlamingockException; +import io.flamingock.internal.common.core.recovery.action.ChangeAction; +import io.flamingock.internal.core.runtime.ExecutionRuntime; +import io.flamingock.internal.core.task.loaded.SteppableTemplateLoadedChange; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class SteppableTemplateExecutableTaskTest { + + private ExecutionRuntime mockRuntime; + private SteppableTemplateLoadedChange mockDescriptor; + private Method applyMethod; + private Method rollbackMethod; + + // Track payloads for verification + private static List appliedPayloads; + private static List rolledBackPayloads; + private static boolean shouldFailOnApply; + private static int failAtStep; + private static boolean shouldFailOnRollback; + + /** + * Test template that tracks apply and rollback invocations. + */ + public static class TestSteppableTemplate extends AbstractSteppableTemplate { + + public TestSteppableTemplate() { + super(); + } + + @Apply + public void apply() { + if (shouldFailOnApply && appliedPayloads.size() == failAtStep) { + throw new RuntimeException("Simulated apply failure at step " + failAtStep); + } + appliedPayloads.add(applyPayload); + } + + @Rollback + public void rollback() { + if (shouldFailOnRollback) { + throw new RuntimeException("Simulated rollback failure"); + } + rolledBackPayloads.add(rollbackPayload); + } + } + + /** + * Test template without rollback method. + */ + public static class TestTemplateWithoutRollback extends AbstractSteppableTemplate { + + public TestTemplateWithoutRollback() { + super(); + } + + @Apply + public void apply() { + if (shouldFailOnApply && appliedPayloads.size() == failAtStep) { + throw new RuntimeException("Simulated apply failure at step " + failAtStep); + } + appliedPayloads.add(applyPayload); + } + } + + @BeforeEach + @SuppressWarnings("unchecked") + void setUp() throws NoSuchMethodException { + // Reset static tracking lists + appliedPayloads = new ArrayList<>(); + rolledBackPayloads = new ArrayList<>(); + shouldFailOnApply = false; + failAtStep = -1; + shouldFailOnRollback = false; + + mockRuntime = mock(ExecutionRuntime.class); + mockDescriptor = mock(SteppableTemplateLoadedChange.class); + + when(mockDescriptor.getId()).thenReturn("test-change-id"); + when(mockDescriptor.isTransactional()).thenReturn(true); + when(mockDescriptor.getConfiguration()).thenReturn(null); + when(mockDescriptor.getTemplateClass()).thenReturn((Class) TestSteppableTemplate.class); + + applyMethod = TestSteppableTemplate.class.getMethod("apply"); + rollbackMethod = TestSteppableTemplate.class.getMethod("rollback"); + + // Setup mock to return real template instance and invoke real methods + Constructor constructor = TestSteppableTemplate.class.getConstructor(); + doReturn(constructor).when(mockDescriptor).getConstructor(); + + when(mockRuntime.getInstance(any(Constructor.class))).thenAnswer(invocation -> { + Constructor ctor = invocation.getArgument(0); + return ctor.newInstance(); + }); + + doAnswer((InvocationOnMock invocation) -> { + Object instance = invocation.getArgument(0); + Method method = invocation.getArgument(1); + try { + return method.invoke(instance); + } catch (InvocationTargetException e) { + // Mimic ExecutionRuntime behavior - wrap in FlamingockException + throw FlamingockException.toFlamingockException(e); + } + }).when(mockRuntime).executeMethodWithInjectedDependencies(any(), any(Method.class)); + } + + @Test + @DisplayName("Should apply all steps in sequence (step 0, 1, 2)") + void shouldApplyAllStepsInOrder() { + // Given + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1"), + new TemplateStep<>("apply-2", "rollback-2") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When + task.apply(mockRuntime); + + // Then + assertEquals(3, appliedPayloads.size()); + assertEquals("apply-0", appliedPayloads.get(0)); + assertEquals("apply-1", appliedPayloads.get(1)); + assertEquals("apply-2", appliedPayloads.get(2)); + } + + @Test + @DisplayName("Should rollback from failed step in reverse order") + void shouldRollbackFromFailedStepInReverseOrder() { + // Given + shouldFailOnApply = true; + failAtStep = 2; // Fail at step index 2 (third step) + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1"), + new TemplateStep<>("apply-2", "rollback-2") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When - apply fails at step 2 + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + + // Then - verify steps 0 and 1 were applied (step 2 failed before completing) + assertEquals(2, appliedPayloads.size()); + assertEquals("apply-0", appliedPayloads.get(0)); + assertEquals("apply-1", appliedPayloads.get(1)); + + // When - rollback from stepIndex=2 (the failed step) down to 0 + task.rollback(mockRuntime); + + // Then - should rollback in reverse order (2, 1, 0) + // Note: step 2's rollback is attempted even though apply failed, because stepIndex was incremented before apply + assertEquals(3, rolledBackPayloads.size()); + assertEquals("rollback-2", rolledBackPayloads.get(0)); + assertEquals("rollback-1", rolledBackPayloads.get(1)); + assertEquals("rollback-0", rolledBackPayloads.get(2)); + } + + @Test + @DisplayName("Should set correct apply payload for each step during apply") + void shouldSetCorrectPayloadDuringApply() { + // Given + List> steps = Arrays.asList( + new TemplateStep<>("payload-A", "rollback-A"), + new TemplateStep<>("payload-B", "rollback-B") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When + task.apply(mockRuntime); + + // Then - payloads should be set correctly for each step + assertEquals(2, appliedPayloads.size()); + assertEquals("payload-A", appliedPayloads.get(0)); + assertEquals("payload-B", appliedPayloads.get(1)); + } + + @Test + @DisplayName("Should set correct rollback payload for each step during rollback") + void shouldSetCorrectPayloadDuringRollback() { + // Given - simulate failure at step 2 so we can rollback steps 0, 1, and 2 + shouldFailOnApply = true; + failAtStep = 2; + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-payload-X"), + new TemplateStep<>("apply-1", "rollback-payload-Y"), + new TemplateStep<>("apply-2", "rollback-payload-Z") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When - apply fails at step 2 + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + task.rollback(mockRuntime); + + // Then - rollback payloads should be set correctly (reverse order: 2, 1, 0) + // stepIndex is 2 when failure occurs, so rollback starts from step 2 + assertEquals(3, rolledBackPayloads.size()); + assertEquals("rollback-payload-Z", rolledBackPayloads.get(0)); // step 2 + assertEquals("rollback-payload-Y", rolledBackPayloads.get(1)); // step 1 + assertEquals("rollback-payload-X", rolledBackPayloads.get(2)); // step 0 + } + + @Test + @DisplayName("Should skip steps without rollback payload during rollback") + void shouldSkipStepsWithoutRollbackPayload() { + // Given - step 1 has no rollback payload, and we have 4 steps to allow failAtStep=3 + shouldFailOnApply = true; + failAtStep = 3; // Fail at step index 3 (fourth step) + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", null), // No rollback payload - should be skipped + new TemplateStep<>("apply-2", "rollback-2"), + new TemplateStep<>("apply-3", "rollback-3") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When - apply fails at step 3 (after steps 0, 1, 2 succeed) + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + + // Verify 3 steps were applied + assertEquals(3, appliedPayloads.size()); + + task.rollback(mockRuntime); + + // Then - should rollback steps 3, 2, 0 (step 1 skipped due to null rollback payload) + // stepIndex=3 when failure occurs, rollback: 3, 2, 1(skipped), 0 + assertEquals(3, rolledBackPayloads.size()); + assertEquals("rollback-3", rolledBackPayloads.get(0)); // step 3 + assertEquals("rollback-2", rolledBackPayloads.get(1)); // step 2 + assertEquals("rollback-0", rolledBackPayloads.get(2)); // step 0 (step 1 skipped) + } + + @Test + @DisplayName("Should handle empty steps list without error") + void shouldHandleEmptyStepsList() { + // Given + List> emptySteps = Collections.emptyList(); + when(mockDescriptor.getSteps()).thenReturn(emptySteps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When & Then - should not throw + assertDoesNotThrow(() -> task.apply(mockRuntime)); + assertEquals(0, appliedPayloads.size()); + + // Rollback should also work with empty list + assertDoesNotThrow(() -> task.rollback(mockRuntime)); + assertEquals(0, rolledBackPayloads.size()); + } + + @Test + @DisplayName("Should not rollback when rollbackMethod is null") + @SuppressWarnings("unchecked") + void shouldNotRollbackWhenRollbackMethodIsNull() throws Exception { + // Given - use template without rollback method + doReturn(TestTemplateWithoutRollback.class).when(mockDescriptor).getTemplateClass(); + Constructor constructor = TestTemplateWithoutRollback.class.getConstructor(); + doReturn(constructor).when(mockDescriptor).getConstructor(); + + Method applyOnlyMethod = TestTemplateWithoutRollback.class.getMethod("apply"); + + shouldFailOnApply = true; + failAtStep = 1; + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyOnlyMethod, + null // No rollback method + ); + + // When + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + task.rollback(mockRuntime); + + // Then - no rollback should have happened + assertEquals(0, rolledBackPayloads.size()); + } + + @Test + @DisplayName("Should throw ChangeExecutionException when apply fails") + void shouldThrowChangeExecutionExceptionOnApplyFailure() { + // Given + shouldFailOnApply = true; + failAtStep = 1; + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When & Then + ChangeExecutionException exception = assertThrows( + ChangeExecutionException.class, + () -> task.apply(mockRuntime) + ); + + assertEquals("test-change-id", exception.getChangeId()); + assertTrue(exception.getMessage().contains("Simulated apply failure")); + } + + @Test + @DisplayName("Should throw ChangeExecutionException when rollback fails") + void shouldThrowChangeExecutionExceptionOnRollbackFailure() { + // Given + shouldFailOnApply = true; + failAtStep = 1; + shouldFailOnRollback = true; + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When - apply fails + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + + // When & Then - rollback also fails + ChangeExecutionException exception = assertThrows( + ChangeExecutionException.class, + () -> task.rollback(mockRuntime) + ); + + assertEquals("test-change-id", exception.getChangeId()); + assertTrue(exception.getMessage().contains("Simulated rollback failure")); + } + + @Test + @DisplayName("Should maintain stepIndex across apply and rollback") + void shouldMaintainStepIndexAcrossApplyAndRollback() { + // Given - fail at step 2 to verify stepIndex state + shouldFailOnApply = true; + failAtStep = 2; + + List> steps = Arrays.asList( + new TemplateStep<>("apply-0", "rollback-0"), + new TemplateStep<>("apply-1", "rollback-1"), + new TemplateStep<>("apply-2", "rollback-2"), + new TemplateStep<>("apply-3", "rollback-3") + ); + when(mockDescriptor.getSteps()).thenReturn(steps); + + SteppableTemplateExecutableTask task = new SteppableTemplateExecutableTask<>( + "test-stage", + mockDescriptor, + ChangeAction.APPLY, + applyMethod, + rollbackMethod + ); + + // When - apply fails at step 2 + assertThrows(ChangeExecutionException.class, () -> task.apply(mockRuntime)); + + // Verify - only steps 0 and 1 were applied (step 2 failed before completion) + assertEquals(2, appliedPayloads.size()); + + // When - rollback + task.rollback(mockRuntime); + + // Then - steps 2, 1, 0 should be rolled back + // stepIndex=2 when failure occurs (incremented before execute), so rollback starts from 2 + assertEquals(3, rolledBackPayloads.size()); + assertEquals("rollback-2", rolledBackPayloads.get(0)); + assertEquals("rollback-1", rolledBackPayloads.get(1)); + assertEquals("rollback-0", rolledBackPayloads.get(2)); + } +} From b6f1922c48419248cc9b0a6d96be3eac54098893 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 22:32:04 +0000 Subject: [PATCH 18/19] refactor: @ChangeTemplate --- .../api/annotations/ChangeTemplate.java | 69 +++++++++++++++++ .../api/template/AbstractChangeTemplate.java | 20 +++-- .../api/template/AbstractSimpleTemplate.java | 27 ------- .../template/AbstractSteppableTemplate.java | 76 ------------------- .../api/template/ChangeTemplate.java | 17 +++-- ...ctChangeTemplateReflectiveClassesTest.java | 10 ++- .../core/template/TemplateValidator.java | 30 ++++---- .../core/template/TemplateValidatorTest.java | 14 ++-- .../SimpleTemplateExecutableTask.java | 6 +- .../SteppableTemplateExecutableTask.java | 3 +- .../loaded/SimpleTemplateLoadedChange.java | 6 +- .../loaded/SteppableTemplateLoadedChange.java | 6 +- .../loaded/TemplateLoadedTaskBuilder.java | 35 ++++----- .../SteppableTemplateExecutableTaskTest.java | 9 ++- .../SimpleTemplateLoadedTaskBuilderTest.java | 8 +- ...teppableTemplateLoadedTaskBuilderTest.java | 8 +- .../graalvm/RegistrationFeature.java | 4 - .../SpringProfileFilterTemplateTaskTest.java | 6 +- 18 files changed, 168 insertions(+), 186 deletions(-) create mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java delete mode 100644 core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java new file mode 100644 index 000000000..a9ce797a3 --- /dev/null +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.api.annotations; + +import io.flamingock.api.template.AbstractChangeTemplate; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a class as a Flamingock change template and configures its execution mode. + * + *

    All template classes must extend {@link AbstractChangeTemplate} and be annotated with + * this annotation to specify whether they process single or multiple steps. + * + *

    Simple templates (default, {@code steppable = false}): + *

    + * id: create-users-table
    + * template: SqlTemplate
    + * apply: "CREATE TABLE users (id INT PRIMARY KEY)"
    + * rollback: "DROP TABLE users"
    + * 
    + * + *

    Steppable templates ({@code steppable = true}) process multiple operations: + *

    + * id: setup-orders
    + * template: MongoTemplate
    + * steps:
    + *   - apply: { type: createCollection, collection: orders }
    + *     rollback: { type: dropCollection, collection: orders }
    + *   - apply: { type: insert, collection: orders, ... }
    + *     rollback: { type: delete, collection: orders, ... }
    + * 
    + * + *

    Steppable rollback behavior: + *

      + *
    • On failure, previously successful steps are rolled back in reverse order
    • + *
    • Steps without rollback are skipped during rollback
    • + *
    + * + * @see AbstractChangeTemplate + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ChangeTemplate { + + /** + * When {@code true}, the template expects a {@code steps} array in YAML. + * When {@code false} (default), it expects {@code apply} and optional {@code rollback} at root. + * + * @return {@code true} for steppable templates, {@code false} for simple templates + */ + boolean steppable() default false; +} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java index 77b5222a5..1a278531d 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractChangeTemplate.java @@ -24,16 +24,20 @@ /** - * Abstract base class for change templates providing common functionality. + * Base class for creating Flamingock change templates. * - *

    This class handles generic type resolution and provides the common fields - * needed by all templates: changeId, isTransactional, and configuration. + *

    Extend this class and annotate with {@link io.flamingock.api.annotations.ChangeTemplate} + * to create custom templates. The annotation's {@code steppable} attribute determines the YAML structure. * - *

    For new templates, extend one of the specialized abstract classes: - *

      - *
    • {@link AbstractSimpleTemplate} - for templates with a single apply/rollback step
    • - *
    • {@link AbstractSteppableTemplate} - for templates with multiple steps
    • - *
    + *

    Use {@code @ChangeTemplate} (default {@code steppable = false}) for simple templates with + * single apply/rollback fields. + * + *

    Use {@code @ChangeTemplate(steppable = true)} for steppable templates with multiple steps. + * + * @param shared configuration type (use {@code Void} if none) + * @param apply payload type + * @param rollback payload type + * @see io.flamingock.api.annotations.ChangeTemplate */ public abstract class AbstractChangeTemplate implements ChangeTemplate { diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java deleted file mode 100644 index af9f49899..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSimpleTemplate.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.flamingock.api.template; - - -public abstract class AbstractSimpleTemplate - extends AbstractChangeTemplate { - - - public AbstractSimpleTemplate(Class... additionalReflectiveClass) { - super(additionalReflectiveClass); - } - -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java deleted file mode 100644 index d5c0b6cc2..000000000 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/AbstractSteppableTemplate.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2025 Flamingock (https://www.flamingock.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.flamingock.api.template; - -import io.flamingock.internal.util.NotThreadSafe; - -import java.util.ArrayList; -import java.util.List; - -/** - * Abstract base class for templates with multiple steps. - * - *

    Use this class when your template processes multiple operations, each with - * its own apply and optional rollback. The YAML structure for this template type is: - * - *

    {@code
    - * id: create-orders-collection
    - * template: MongoChangeTemplate
    - * steps:
    - *   - apply:
    - *       type: createCollection
    - *       collection: orders
    - *     rollback:
    - *       type: dropCollection
    - *       collection: orders
    - *   - apply:
    - *       type: insert
    - *       collection: orders
    - *       parameters:
    - *         documents:
    - *           - orderId: "ORD-001"
    - *     rollback:
    - *       type: delete
    - *       collection: orders
    - *       parameters:
    - *         filter: {}
    - * }
    - * - * - *

    Rollback Behavior: - *

      - *
    • When a step fails, all previously successful steps are rolled back in reverse order
    • - *
    • Steps without rollback operations are skipped during rollback
    • - *
    • Rollback errors are logged but don't stop the rollback process
    • - *
    - * - * @param the type of shared configuration - * @param the type of the apply payload for each step - * @param the type of the rollback payload for each step - */ -@NotThreadSafe -public abstract class AbstractSteppableTemplate - extends AbstractChangeTemplate { - - - public AbstractSteppableTemplate(Class... additionalReflectiveClass) { - super(additionalReflectiveClass); - } - - - - -} diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java index 9ac70af61..cdc0cbde0 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/template/ChangeTemplate.java @@ -16,16 +16,17 @@ package io.flamingock.api.template; /** - * Interface representing a reusable change template with configuration of type {@code CONFIG}. + * Core interface for Flamingock change templates. * - *

    This interface is commonly implemented by classes that act as templates for Changes - * where a specific configuration needs to be injected and managed independently. + *

    Templates enable declarative, YAML-based changes. Implement this interface + * by extending {@link AbstractChangeTemplate} and annotating with + * {@link io.flamingock.api.annotations.ChangeTemplate}. * - *

    Templates should extend one of the abstract base classes: - *

      - *
    • {@link AbstractSimpleTemplate} - for templates with a single apply/rollback step
    • - *
    • {@link AbstractSteppableTemplate} - for templates with multiple steps
    • - *
    + * @param shared configuration type (use {@code Void} if none) + * @param apply payload type parsed from YAML + * @param rollback payload type parsed from YAML + * @see AbstractChangeTemplate + * @see io.flamingock.api.annotations.ChangeTemplate */ public interface ChangeTemplate extends ReflectionMetadataProvider { diff --git a/core/flamingock-core-api/src/test/java/io/flamingock/api/template/AbstractChangeTemplateReflectiveClassesTest.java b/core/flamingock-core-api/src/test/java/io/flamingock/api/template/AbstractChangeTemplateReflectiveClassesTest.java index 5b38e9564..6227c5a24 100644 --- a/core/flamingock-core-api/src/test/java/io/flamingock/api/template/AbstractChangeTemplateReflectiveClassesTest.java +++ b/core/flamingock-core-api/src/test/java/io/flamingock/api/template/AbstractChangeTemplateReflectiveClassesTest.java @@ -16,6 +16,7 @@ package io.flamingock.api.template; import io.flamingock.api.annotations.Apply; +import io.flamingock.api.annotations.ChangeTemplate; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -51,8 +52,9 @@ public static class AnotherAdditionalClass { } // Test template with custom generic types + @ChangeTemplate public static class TestTemplateWithCustomTypes - extends AbstractSimpleTemplate { + extends AbstractChangeTemplate { public TestTemplateWithCustomTypes() { super(); @@ -65,8 +67,9 @@ public void apply() { } // Test template with additional reflective classes + @ChangeTemplate public static class TestTemplateWithAdditionalClasses - extends AbstractSimpleTemplate { + extends AbstractChangeTemplate { public TestTemplateWithAdditionalClasses() { super(AdditionalClass.class, AnotherAdditionalClass.class); @@ -79,8 +82,9 @@ public void apply() { } // Test template with Void configuration + @ChangeTemplate public static class TestTemplateWithVoidConfig - extends AbstractSimpleTemplate { + extends AbstractChangeTemplate { public TestTemplateWithVoidConfig() { super(); diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java index 667f23064..443fc4705 100644 --- a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java +++ b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java @@ -15,9 +15,7 @@ */ package io.flamingock.internal.common.core.template; -import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.api.annotations.ChangeTemplate; import io.flamingock.internal.common.core.error.validation.ValidationError; import io.flamingock.internal.common.core.error.validation.ValidationResult; @@ -49,15 +47,17 @@ public class TemplateValidator { */ public enum TemplateType { /** - * Template extends AbstractSimpleTemplate - uses apply/rollback fields. + * Template annotated with {@code @ChangeTemplate(steppable = false)} or without annotation. + * Uses apply/rollback fields. */ SIMPLE, /** - * Template extends AbstractSteppableTemplate - uses steps field. + * Template annotated with {@code @ChangeTemplate(steppable = true)}. + * Uses steps field. */ STEPPABLE, /** - * Template type could not be determined. + * Template type could not be determined (kept for backward compatibility). */ UNKNOWN } @@ -88,7 +88,7 @@ public ValidationResult validate(ChangeTemplateFileContent content) { return result; } - Optional>> templateClassOpt = ChangeTemplateManager.getTemplate(templateName); + Optional>> templateClassOpt = ChangeTemplateManager.getTemplate(templateName); if (!templateClassOpt.isPresent()) { result.add(new ValidationError( @@ -99,7 +99,7 @@ public ValidationResult validate(ChangeTemplateFileContent content) { return result; } - Class> templateClass = templateClassOpt.get(); + Class> templateClass = templateClassOpt.get(); TemplateType type = getTemplateType(templateClass); switch (type) { @@ -118,18 +118,18 @@ public ValidationResult validate(ChangeTemplateFileContent content) { } /** - * Determines the template type based on the class hierarchy. + * Determines the template type based on the {@link ChangeTemplate} annotation. * * @param templateClass the template class to check - * @return the TemplateType (SIMPLE, STEPPABLE, or UNKNOWN) + * @return the TemplateType (SIMPLE or STEPPABLE). Returns SIMPLE by default if annotation is missing. */ - public TemplateType getTemplateType(Class> templateClass) { - if (AbstractSimpleTemplate.class.isAssignableFrom(templateClass)) { - return TemplateType.SIMPLE; - } else if (AbstractSteppableTemplate.class.isAssignableFrom(templateClass)) { + public TemplateType getTemplateType(Class> templateClass) { + ChangeTemplate annotation = templateClass.getAnnotation(ChangeTemplate.class); + if (annotation != null && annotation.steppable()) { return TemplateType.STEPPABLE; } - return TemplateType.UNKNOWN; + // Default to SIMPLE (including when annotation is missing or steppable=false) + return TemplateType.SIMPLE; } /** diff --git a/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java b/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java index 82975e039..fc93a42bc 100644 --- a/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java +++ b/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java @@ -16,8 +16,8 @@ package io.flamingock.internal.common.core.template; import io.flamingock.api.annotations.Apply; -import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.annotations.ChangeTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.internal.common.core.error.validation.ValidationResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -36,8 +36,9 @@ class TemplateValidatorTest { private TemplateValidator validator; - // Test template extending AbstractSimpleTemplate - public static class TestSimpleTemplate extends AbstractSimpleTemplate { + // Test template with @ChangeTemplate (simple template) + @ChangeTemplate + public static class TestSimpleTemplate extends AbstractChangeTemplate { public TestSimpleTemplate() { super(); } @@ -48,8 +49,9 @@ public void apply() { } } - // Test template extending AbstractSteppableTemplate - public static class TestSteppableTemplate extends AbstractSteppableTemplate { + // Test template with @ChangeTemplate(steppable = true) + @ChangeTemplate(steppable = true) + public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() { super(); } diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java index 2bea0c909..5add1dad2 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SimpleTemplateExecutableTask.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.task.executable; -import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; import io.flamingock.internal.core.runtime.ExecutionRuntime; @@ -25,7 +25,7 @@ /** * Executable task for simple templates (single apply/rollback step). - * Handles templates extending {@link AbstractSimpleTemplate}. + * Handles templates annotated with {@code @ChangeTemplate(steppable = false)} or without annotation. * * @param the configuration type for the template * @param the apply payload type @@ -59,7 +59,7 @@ public void rollback(ExecutionRuntime executionRuntime) { @SuppressWarnings("unchecked") protected void executeInternal(ExecutionRuntime executionRuntime, Method method) { try { - AbstractSimpleTemplate instance = (AbstractSimpleTemplate) + AbstractChangeTemplate instance = (AbstractChangeTemplate) executionRuntime.getInstance(descriptor.getConstructor()); instance.setTransactional(descriptor.isTransactional()); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java index e218100eb..0b735e054 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTask.java @@ -16,7 +16,6 @@ package io.flamingock.internal.core.task.executable; import io.flamingock.api.template.AbstractChangeTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.recovery.action.ChangeAction; @@ -29,7 +28,7 @@ /** * Executable task for steppable templates (multiple steps). - * Handles templates extending {@link AbstractSteppableTemplate}. + * Handles templates annotated with {@code @ChangeTemplate(steppable = true)}. * * @param the configuration type for the template * @param the apply payload type diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java index 5ec4ea209..6abea2546 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedChange.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.task.loaded; -import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -24,7 +24,7 @@ /** * Loaded change for simple templates (single apply/rollback step). - * Used for templates extending {@link io.flamingock.api.template.AbstractSimpleTemplate}. + * Used for templates annotated with {@code @ChangeTemplate(steppable = false)} or without annotation. *

    * The payloads are converted from raw YAML data (Object/Map) to typed values * at load time, enabling early validation and cleaner executable tasks. @@ -44,7 +44,7 @@ public class SimpleTemplateLoadedChange String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java index ec85d151b..3bd42660c 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedChange.java @@ -15,7 +15,7 @@ */ package io.flamingock.internal.core.task.loaded; -import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.task.RecoveryDescriptor; import io.flamingock.internal.common.core.task.TargetSystemDescriptor; @@ -25,7 +25,7 @@ /** * Loaded change for steppable templates (multiple steps). - * Used for templates extending {@link io.flamingock.api.template.AbstractSteppableTemplate}. + * Used for templates annotated with {@code @ChangeTemplate(steppable = true)}. *

    * The steps are converted from raw YAML data (List of Maps) to typed TemplateStep objects * at load time, enabling early validation and cleaner executable tasks. @@ -43,7 +43,7 @@ public class SteppableTemplateLoadedChange String id, String order, String author, - Class> templateClass, + Class> templateClass, Constructor constructor, List profiles, boolean transactional, diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index 4bba5918b..75e341139 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -15,9 +15,8 @@ */ package io.flamingock.internal.core.task.loaded; -import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; -import io.flamingock.api.template.ChangeTemplate; +import io.flamingock.api.annotations.ChangeTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.preview.AbstractPreviewTask; @@ -150,20 +149,22 @@ public TemplateLoadedTaskBuilder setSteps(Object steps) { @SuppressWarnings("unchecked") public AbstractTemplateLoadedChange build() { // boolean isTaskTransactional = true;//TODO implement this. isTaskTransactionalAccordingTemplate(templateSpec); - Class> templateClass = ChangeTemplateManager.getTemplate(templateName) + Class> templateClass = ChangeTemplateManager.getTemplate(templateName) .orElseThrow(()-> new FlamingockException(String.format("Template[%s] not found. This is probably because template's name is wrong or template's library not imported", templateName))); Constructor constructor = ReflectionUtil.getDefaultConstructor(templateClass); - // Determine template type and build appropriate loaded change + // Determine template type based on @ChangeTemplate annotation // Note: Due to type erasure, we use Object bounds at construction time. // The actual type safety comes from the conversion methods that use reflection // to determine the real types at runtime. - if (AbstractSteppableTemplate.class.isAssignableFrom(templateClass)) { + ChangeTemplate annotation = templateClass.getAnnotation(ChangeTemplate.class); + boolean isSteppable = annotation != null && annotation.steppable(); - Class> steppableTemplateClass = - (Class>) - templateClass.asSubclass(AbstractSteppableTemplate.class); + if (isSteppable) { + Class> steppableTemplateClass = + (Class>) + templateClass.asSubclass(AbstractChangeTemplate.class); // Convert steps at load time List> convertedSteps = convertSteps(constructor, steps); @@ -184,10 +185,10 @@ public TemplateLoadedTaskBuilder setSteps(Object steps) { targetSystem, recovery); } else { - // Default to SimpleTemplateLoadedChange for AbstractSimpleTemplate and unknown types - Class> simpleTemplateClass = - (Class>) - templateClass.asSubclass(AbstractSimpleTemplate.class); + // Default to SimpleTemplateLoadedChange for simple templates (steppable=false or missing annotation) + Class> simpleTemplateClass = + (Class>) + templateClass.asSubclass(AbstractChangeTemplate.class); // Convert apply/rollback to typed payloads at load time Pair convertedPayloads = convertPayloads(constructor, apply, rollback); @@ -222,9 +223,9 @@ private Pair convertPayloads(Constructor constructor, Object } // Instantiate template temporarily to get payload types - AbstractSimpleTemplate templateInstance; + AbstractChangeTemplate templateInstance; try { - templateInstance = (AbstractSimpleTemplate) constructor.newInstance(); + templateInstance = (AbstractChangeTemplate) constructor.newInstance(); } catch (Exception e) { throw new FlamingockException("Failed to instantiate template for type resolution: " + e.getMessage(), e); } @@ -263,9 +264,9 @@ private List> convertSteps(Constructor construct } // Instantiate template temporarily to get payload types - AbstractSteppableTemplate templateInstance; + AbstractChangeTemplate templateInstance; try { - templateInstance = (AbstractSteppableTemplate) constructor.newInstance(); + templateInstance = (AbstractChangeTemplate) constructor.newInstance(); } catch (Exception e) { throw new FlamingockException("Failed to instantiate template for type resolution: " + e.getMessage(), e); } diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java index fadc377e4..51e43cf02 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java @@ -16,8 +16,9 @@ package io.flamingock.internal.core.task.executable; import io.flamingock.api.annotations.Apply; +import io.flamingock.api.annotations.ChangeTemplate; import io.flamingock.api.annotations.Rollback; -import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.ChangeExecutionException; import io.flamingock.internal.common.core.error.FlamingockException; @@ -57,7 +58,8 @@ class SteppableTemplateExecutableTaskTest { /** * Test template that tracks apply and rollback invocations. */ - public static class TestSteppableTemplate extends AbstractSteppableTemplate { + @ChangeTemplate(steppable = true) + public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() { super(); @@ -83,7 +85,8 @@ public void rollback() { /** * Test template without rollback method. */ - public static class TestTemplateWithoutRollback extends AbstractSteppableTemplate { + @ChangeTemplate(steppable = true) + public static class TestTemplateWithoutRollback extends AbstractChangeTemplate { public TestTemplateWithoutRollback() { super(); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java index 99661d64e..db8de7b72 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SimpleTemplateLoadedTaskBuilderTest.java @@ -17,7 +17,8 @@ import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.template.ChangeTemplateManager; -import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.annotations.ChangeTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.annotations.Apply; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -34,8 +35,9 @@ class SimpleTemplateLoadedTaskBuilderTest { private TemplateLoadedTaskBuilder builder; - // Simple test template implementation using the abstract class - public static class TestChangeTemplate extends AbstractSimpleTemplate { + // Simple test template implementation using the annotation + @ChangeTemplate + public static class TestChangeTemplate extends AbstractChangeTemplate { public TestChangeTemplate() { super(); diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index 3308ffc89..f671504d4 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -16,7 +16,8 @@ package io.flamingock.internal.core.task.loaded; import io.flamingock.api.annotations.Apply; -import io.flamingock.api.template.AbstractSteppableTemplate; +import io.flamingock.api.annotations.ChangeTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.template.ChangeTemplateManager; @@ -39,8 +40,9 @@ class SteppableTemplateLoadedTaskBuilderTest { private TemplateLoadedTaskBuilder builder; - // Steppable test template implementation using the abstract class - public static class TestSteppableTemplate extends AbstractSteppableTemplate { + // Steppable test template implementation using the annotation + @ChangeTemplate(steppable = true) + public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() { super(); diff --git a/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java b/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java index 888f0de05..229b0da89 100644 --- a/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java +++ b/core/flamingock-graalvm/src/main/java/io/flamingock/graalvm/RegistrationFeature.java @@ -16,8 +16,6 @@ package io.flamingock.graalvm; import io.flamingock.api.template.AbstractChangeTemplate; -import io.flamingock.api.template.AbstractSimpleTemplate; -import io.flamingock.api.template.AbstractSteppableTemplate; import io.flamingock.api.template.ChangeTemplate; import io.flamingock.api.template.TemplateStep; import io.flamingock.internal.common.core.metadata.FlamingockMetadata; @@ -151,8 +149,6 @@ private void registerTemplates() { registerClassForReflection(ChangeTemplateManager.class); registerClassForReflection(ChangeTemplate.class); registerClassForReflection(AbstractChangeTemplate.class); - registerClassForReflection(AbstractSimpleTemplate.class); - registerClassForReflection(AbstractSteppableTemplate.class); registerClassForReflection(TemplateStep.class); ChangeTemplateManager.getTemplates().forEach(template -> { registerClassForReflection(template.getClass()); diff --git a/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/SpringProfileFilterTemplateTaskTest.java b/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/SpringProfileFilterTemplateTaskTest.java index 5cc9da350..60c477696 100644 --- a/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/SpringProfileFilterTemplateTaskTest.java +++ b/platform-plugins/flamingock-springboot-integration/src/test/java/io/flamingock/springboot/SpringProfileFilterTemplateTaskTest.java @@ -16,9 +16,10 @@ package io.flamingock.springboot; import io.flamingock.api.annotations.Change; +import io.flamingock.api.annotations.ChangeTemplate; import io.flamingock.internal.common.core.template.ChangeTemplateFileContent; import io.flamingock.internal.common.core.task.RecoveryDescriptor; -import io.flamingock.api.template.AbstractSimpleTemplate; +import io.flamingock.api.template.AbstractChangeTemplate; import io.flamingock.internal.common.core.template.ChangeTemplateManager; import io.flamingock.internal.common.core.preview.TemplatePreviewChange; import io.flamingock.internal.common.core.preview.builder.PreviewTaskBuilder; @@ -126,7 +127,8 @@ private AbstractLoadedTask getTemplateLoadedChange(String profiles) { } - public static abstract class TemplateSimulate extends AbstractSimpleTemplate { + @ChangeTemplate + public static abstract class TemplateSimulate extends AbstractChangeTemplate { public TemplateSimulate() { super(); } From 9b746a76df4195c4422e71f2efa8bcd6056078d1 Mon Sep 17 00:00:00 2001 From: Antonio Perez Dieppa Date: Fri, 13 Feb 2026 23:07:20 +0000 Subject: [PATCH 19/19] refactor: @ChangeTemplate --- .../java/io/flamingock/api/annotations/ChangeTemplate.java | 2 +- .../internal/common/core/template/TemplateValidator.java | 2 +- .../internal/common/core/template/TemplateValidatorTest.java | 2 +- .../internal/core/task/loaded/TemplateLoadedTaskBuilder.java | 2 +- .../task/executable/SteppableTemplateExecutableTaskTest.java | 4 ++-- .../task/loaded/SteppableTemplateLoadedTaskBuilderTest.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java index a9ce797a3..fc2239fae 100644 --- a/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java +++ b/core/flamingock-core-api/src/main/java/io/flamingock/api/annotations/ChangeTemplate.java @@ -65,5 +65,5 @@ * * @return {@code true} for steppable templates, {@code false} for simple templates */ - boolean steppable() default false; + boolean multiStep() default false; } diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java index 443fc4705..055314921 100644 --- a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java +++ b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/template/TemplateValidator.java @@ -125,7 +125,7 @@ public ValidationResult validate(ChangeTemplateFileContent content) { */ public TemplateType getTemplateType(Class> templateClass) { ChangeTemplate annotation = templateClass.getAnnotation(ChangeTemplate.class); - if (annotation != null && annotation.steppable()) { + if (annotation != null && annotation.multiStep()) { return TemplateType.STEPPABLE; } // Default to SIMPLE (including when annotation is missing or steppable=false) diff --git a/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java b/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java index fc93a42bc..ba399f8e6 100644 --- a/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java +++ b/core/flamingock-core-commons/src/test/java/io/flamingock/internal/common/core/template/TemplateValidatorTest.java @@ -50,7 +50,7 @@ public void apply() { } // Test template with @ChangeTemplate(steppable = true) - @ChangeTemplate(steppable = true) + @ChangeTemplate(multiStep = true) public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() { super(); diff --git a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java index 75e341139..5ad6831e5 100644 --- a/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java +++ b/core/flamingock-core/src/main/java/io/flamingock/internal/core/task/loaded/TemplateLoadedTaskBuilder.java @@ -159,7 +159,7 @@ public TemplateLoadedTaskBuilder setSteps(Object steps) { // The actual type safety comes from the conversion methods that use reflection // to determine the real types at runtime. ChangeTemplate annotation = templateClass.getAnnotation(ChangeTemplate.class); - boolean isSteppable = annotation != null && annotation.steppable(); + boolean isSteppable = annotation != null && annotation.multiStep(); if (isSteppable) { Class> steppableTemplateClass = diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java index 51e43cf02..9d7e344c9 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/executable/SteppableTemplateExecutableTaskTest.java @@ -58,7 +58,7 @@ class SteppableTemplateExecutableTaskTest { /** * Test template that tracks apply and rollback invocations. */ - @ChangeTemplate(steppable = true) + @ChangeTemplate(multiStep = true) public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() { @@ -85,7 +85,7 @@ public void rollback() { /** * Test template without rollback method. */ - @ChangeTemplate(steppable = true) + @ChangeTemplate(multiStep = true) public static class TestTemplateWithoutRollback extends AbstractChangeTemplate { public TestTemplateWithoutRollback() { diff --git a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java index f671504d4..86eb18e0d 100644 --- a/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java +++ b/core/flamingock-core/src/test/java/io/flamingock/internal/core/task/loaded/SteppableTemplateLoadedTaskBuilderTest.java @@ -41,7 +41,7 @@ class SteppableTemplateLoadedTaskBuilderTest { private TemplateLoadedTaskBuilder builder; // Steppable test template implementation using the annotation - @ChangeTemplate(steppable = true) + @ChangeTemplate(multiStep = true) public static class TestSteppableTemplate extends AbstractChangeTemplate { public TestSteppableTemplate() {