diff --git a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/java/org/flowable/spring/boot/FlowableDependsOnDatabaseInitializationDetector.java b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/java/org/flowable/spring/boot/FlowableDependsOnDatabaseInitializationDetector.java
new file mode 100644
index 00000000000..83939e18fb4
--- /dev/null
+++ b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/java/org/flowable/spring/boot/FlowableDependsOnDatabaseInitializationDetector.java
@@ -0,0 +1,46 @@
+/* Licensed under the Apache License, Version 2.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 org.flowable.spring.boot;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.flowable.common.engine.api.Engine;
+import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
+import org.springframework.core.env.Environment;
+
+/**
+ * {@link org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector}
+ * marking all Flowable {@link Engine} beans as depending on database initialization, so Spring
+ * Boot orders them after Flyway, Liquibase or spring.sql.init have run.
+ *
+ *
Can be opted out of by setting {@code flowable.depends-on-database-initialization-detection}
+ * to {@code false}, mirroring the JPA detector's opt-out behavior.
+ *
+ * @author Nikhil Bharadwaj Ramashasthri
+ */
+class FlowableDependsOnDatabaseInitializationDetector extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
+
+ private final Environment environment;
+
+ FlowableDependsOnDatabaseInitializationDetector(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ protected Set> getDependsOnDatabaseInitializationBeanTypes() {
+ boolean detectionEnabled = environment
+ .getProperty("flowable.depends-on-database-initialization-detection", Boolean.class, true);
+ return detectionEnabled ? Collections.singleton(Engine.class) : Collections.emptySet();
+ }
+}
diff --git a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 050d700a1e2..6dbe5a34dd8 100644
--- a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -714,6 +714,12 @@
"name": "flowable.process.async-history.executor.timer-runnable-needed",
"type": "java.lang.Boolean",
"defaultValue": false
+ },
+ {
+ "name": "flowable.depends-on-database-initialization-detection",
+ "type": "java.lang.Boolean",
+ "description": "Whether Flowable engine beans should be marked as depending on Spring Boot managed database initializers (Flyway, Liquibase, spring.sql.init), ensuring they start only after schema initialization has run.",
+ "defaultValue": true
}
],
"hints": [
@@ -761,7 +767,6 @@
"value": "spring_delegating_noop",
"description": "Use the Spring Security 5 PasswordEncoderFactories.createDelegatingPasswordEncoder(). With NoopPasswordEncoder as the default password encoder for matches. This should be used for backwards compatibility with older versions."
}
-
]
},
{
@@ -797,4 +802,4 @@
]
}
]
-}
+}
\ No newline at end of file
diff --git a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
index 6fa3a2dcdb0..df3bcafce56 100644
--- a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,5 @@
org.springframework.boot.EnvironmentPostProcessor=\
org.flowable.spring.boot.environment.FlowableDefaultPropertiesEnvironmentPostProcessor
+org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
+ org.flowable.spring.boot.FlowableDependsOnDatabaseInitializationDetector
diff --git a/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/test/java/org/flowable/test/spring/boot/FlowableDependsOnDatabaseInitializationDetectorTest.java b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/test/java/org/flowable/test/spring/boot/FlowableDependsOnDatabaseInitializationDetectorTest.java
new file mode 100644
index 00000000000..3da7ff2e362
--- /dev/null
+++ b/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/test/java/org/flowable/test/spring/boot/FlowableDependsOnDatabaseInitializationDetectorTest.java
@@ -0,0 +1,77 @@
+/* Licensed under the Apache License, Version 2.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 org.flowable.test.spring.boot;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.flowable.spring.boot.ProcessEngineAutoConfiguration;
+import org.flowable.spring.boot.ProcessEngineServicesAutoConfiguration;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceInitializationAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+/**
+ * The Flowable engines create/validate their own schema on engine build, so their beans must be
+ * ordered after Spring Boot managed database initializers (Flyway, Liquibase, spring.sql.init).
+ * This is done through the {@code DependsOnDatabaseInitializationDetector} SPI (issue #4213).
+ *
+ * @author Nikhil Bharadwaj Ramashasthri
+ */
+class FlowableDependsOnDatabaseInitializationDetectorTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(
+ DataSourceAutoConfiguration.class,
+ DataSourceTransactionManagerAutoConfiguration.class,
+ DataSourceInitializationAutoConfiguration.class,
+ ProcessEngineAutoConfiguration.class,
+ ProcessEngineServicesAutoConfiguration.class));
+
+ @Test
+ void processEngineDependsOnDatabaseInitialization() {
+ contextRunner
+ .withPropertyValues(
+ "spring.sql.init.mode=always",
+ "flowable.check-process-definitions=false")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ String[] dependsOn = context.getBeanFactory()
+ .getBeanDefinition("processEngine")
+ .getDependsOn();
+ assertThat(dependsOn)
+ .as("processEngine must wait for the SQL init database initializer")
+ .contains("dataSourceScriptDatabaseInitializer");
+ });
+ }
+
+ @Test
+ void detectionCanBeOptedOut() {
+ contextRunner
+ .withPropertyValues(
+ "spring.sql.init.mode=always",
+ "flowable.check-process-definitions=false",
+ "flowable.depends-on-database-initialization-detection=false")
+ .run(context -> {
+ assertThat(context).hasNotFailed();
+ String[] dependsOn = context.getBeanFactory()
+ .getBeanDefinition("processEngine")
+ .getDependsOn();
+ assertThat(dependsOn)
+ .as("opt-out must remove the database initializer dependency")
+ .isNullOrEmpty();
+ });
+ }
+}