Skip to content
Merged
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
Expand Up @@ -24,11 +24,13 @@
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import java.util.List;

Expand All @@ -37,11 +39,14 @@
*
* <p>The configuration behavior is controlled by the {@code flamingock.management-mode} property:</p>
* <ul>
* <li>{@code APPLICATION_RUNNER} (default) - Spring creates, builds, and executes the runner as an ApplicationRunner</li>
* <li>{@code INITIALIZING_BEAN} - Spring creates, builds, and executes the runner as an InitializingBean</li>
* <li>{@code APPLICATION_RUNNER} (default) - Spring creates the builder and executes it as an ApplicationRunner</li>
* <li>{@code INITIALIZING_BEAN} - Spring creates the builder and executes it as an InitializingBean</li>
* <li>{@code DEFERRED} - Spring creates the builder; the application controls execution</li>
* <li>{@code UNMANAGED} - No beans are created; the application manages everything</li>
* </ul>
*
* <p>The builder bean is always created (unless UNMANAGED) and can be overridden by providing
* your own {@link AbstractChangeRunnerBuilder} bean.</p>
*/
@Configuration
@ConditionalOnClass(name = "org.springframework.boot.SpringApplication")
Expand All @@ -50,56 +55,16 @@
public class FlamingockAutoConfiguration {

/**
* Creates an ApplicationRunner that builds and executes Flamingock at application startup.
* Only created when management-mode is APPLICATION_RUNNER (the default).
*/
@Bean("flamingock-runner")
@ConditionalOnExpression("'${flamingock.management-mode:APPLICATION_RUNNER}'.toUpperCase().equals('APPLICATION_RUNNER')")
public ApplicationRunner applicationRunner(SpringbootProperties configurationProperties,
ApplicationContext springContext,
ApplicationEventPublisher applicationEventPublisher,
@Autowired(required = false) CommunityAuditStore auditStore,
List<TargetSystem> targetSystems) {
AbstractChangeRunnerBuilder<?, ?> builder = createBuilder(
configurationProperties, springContext, applicationEventPublisher, auditStore, targetSystems);
return SpringbootUtil.toApplicationRunner(builder);
}

/**
* Creates an InitializingBean that builds and executes Flamingock during bean initialization.
* Only created when management-mode is INITIALIZING_BEAN.
*/
@Bean("flamingock-runner")
@ConditionalOnExpression("'${flamingock.management-mode:APPLICATION_RUNNER}'.toUpperCase().equals('INITIALIZING_BEAN')")
public InitializingBean initializingBeanRunner(SpringbootProperties configurationProperties,
ApplicationContext springContext,
ApplicationEventPublisher applicationEventPublisher,
@Autowired(required = false) CommunityAuditStore auditStore,
List<TargetSystem> targetSystems) {
AbstractChangeRunnerBuilder<?, ?> builder = createBuilder(
configurationProperties, springContext, applicationEventPublisher, auditStore, targetSystems);
return SpringbootUtil.toInitializingBean(builder);
}

/**
* Exposes the Flamingock builder for manual control over execution.
* Only created when management-mode is DEFERRED.
* Creates the Flamingock builder bean.
* Always created unless management-mode is UNMANAGED or user provides their own builder.
*/
@Bean("flamingock-builder")
@ConditionalOnExpression("'${flamingock.management-mode:APPLICATION_RUNNER}'.toUpperCase().equals('DEFERRED')")
@ConditionalOnMissingBean(AbstractChangeRunnerBuilder.class)
public AbstractChangeRunnerBuilder<?, ?> flamingockBuilder(SpringbootProperties configurationProperties,
ApplicationContext springContext,
ApplicationEventPublisher applicationEventPublisher,
@Autowired(required = false) CommunityAuditStore auditStore,
List<TargetSystem> targetSystems) {
return createBuilder(configurationProperties, springContext, applicationEventPublisher, auditStore, targetSystems);
}

private AbstractChangeRunnerBuilder<?, ?> createBuilder(SpringbootProperties configurationProperties,
ApplicationContext springContext,
ApplicationEventPublisher applicationEventPublisher,
CommunityAuditStore auditStore,
List<TargetSystem> targetSystems) {
AbstractChangeRunnerBuilder<?, ?> builder = FlamingockFactory.getEditionAwareBuilder(
configurationProperties.getCoreConfiguration(),
configurationProperties.getCloudProperties(),
Expand All @@ -116,4 +81,24 @@ public InitializingBean initializingBeanRunner(SpringbootProperties configuratio

return builder;
}

/**
* Creates an ApplicationRunner that builds and executes Flamingock at application startup.
* Only created when management-mode is APPLICATION_RUNNER (the default).
*/
@Bean("flamingock-runner")
@ConditionalOnExpression("'${flamingock.management-mode:APPLICATION_RUNNER}'.toUpperCase().equals('APPLICATION_RUNNER')")
public ApplicationRunner applicationRunner(AbstractChangeRunnerBuilder<?, ?> builder) {
return SpringbootUtil.toApplicationRunner(builder);
}

/**
* Creates an InitializingBean that builds and executes Flamingock during bean initialization.
* Only created when management-mode is INITIALIZING_BEAN.
*/
@Bean("flamingock-runner")
@ConditionalOnExpression("'${flamingock.management-mode:APPLICATION_RUNNER}'.toUpperCase().equals('INITIALIZING_BEAN')")
public InitializingBean initializingBeanRunner(AbstractChangeRunnerBuilder<?, ?> builder) {
return SpringbootUtil.toInitializingBean(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,29 @@ private ApplicationContextRunner contextRunner() {
}

@Test
void whenModeIsDefault_thenRunnerBeanExistsAndBuilderBeanDoesNot() {
// Default is APPLICATION_RUNNER - runner bean exists, builder bean does not
void whenModeIsDefault_thenBothBuilderAndRunnerBeansExist() {
// Default is APPLICATION_RUNNER - both builder and runner beans exist
contextRunner().run(ctx -> {
assertThat(ctx).hasBean("flamingock-builder");
assertThat(ctx).hasBean("flamingock-runner");
assertThat(ctx).doesNotHaveBean("flamingock-builder");
});
}

@Test
void whenModeIsApplicationRunner_thenRunnerBeanExistsAndBuilderBeanDoesNot() {
void whenModeIsApplicationRunner_thenBothBuilderAndRunnerBeansExist() {
contextRunner()
.withPropertyValues("flamingock.management-mode=APPLICATION_RUNNER")
.run(ctx -> {
assertThat(ctx).hasBean("flamingock-builder");
assertThat(ctx).hasBean("flamingock-runner");
assertThat(ctx).doesNotHaveBean("flamingock-builder");
});
}

@Test
void whenModeIsInitializingBean_thenRunnerBeanIsCreated() {
void whenModeIsInitializingBean_thenBothBeansCreatedAndExecutionAttempted() {
// InitializingBean executes immediately during bean creation.
// We verify the bean creation was attempted by checking the context fails
// with a Flamingock-related error (proving the InitializingBean bean was created and executed).
// Both builder and runner beans are created, but execution fails because
// the test context doesn't have all required Flamingock dependencies.
contextRunner()
.withPropertyValues("flamingock.management-mode=INITIALIZING_BEAN")
.run(ctx -> {
Expand Down
Loading