Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
boolean detectionEnabled = environment
.getProperty("flowable.depends-on-database-initialization-detection", Boolean.class, true);
return detectionEnabled ? Collections.singleton(Engine.class) : Collections.emptySet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down Expand Up @@ -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."
}

]
},
{
Expand Down Expand Up @@ -797,4 +802,4 @@
]
}
]
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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();
});
}
}