Skip to content

Commit

Permalink
Refine container initialization and parallel startup logic
Browse files Browse the repository at this point in the history
Update `TestcontainersLifecycleBeanPostProcessor` to restore early
container initialization logic and refine startup logic. Initial bean
access now again triggers the creation all container beans. In addition
the first access of a `Startable` bean now attempts to find and start
all other `Startable` beans.

Fixes gh-37989
  • Loading branch information
philwebb committed Oct 25, 2023
1 parent efb5cb0 commit 0c66db7
Showing 1 changed file with 45 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
Expand Down Expand Up @@ -62,7 +63,9 @@ class TestcontainersLifecycleBeanPostProcessor implements DestructionAwareBeanPo

private final TestcontainersStartup startup;

private volatile boolean containersInitialized = false;
private final AtomicBoolean startablesInitialized = new AtomicBoolean();

private final AtomicBoolean containersInitialized = new AtomicBoolean();

TestcontainersLifecycleBeanPostProcessor(ConfigurableListableBeanFactory beanFactory,
TestcontainersStartup startup) {
Expand All @@ -72,47 +75,66 @@ class TestcontainersLifecycleBeanPostProcessor implements DestructionAwareBeanPo

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!this.containersInitialized && this.beanFactory.isConfigurationFrozen()) {
if (this.beanFactory.isConfigurationFrozen() && this.containersInitialized.compareAndSet(false, true)) {
initializeContainers();
}
if (bean instanceof Startable startableBean) {
if (this.startablesInitialized.compareAndSet(false, true)) {
initializeStartables(startableBean, beanName);
}
else {
startableBean.start();

This comment has been minimized.

Copy link
@Wzy19930507

Wzy19930507 Oct 25, 2023

Contributor

Hello, @philwebb I,m interesting this line, when start again

This comment has been minimized.

Copy link
@philwebb

philwebb Oct 26, 2023

Author Member

It covers a possible edge case where startablesInitialized has been called already but somehow a new startable bean is post processed. I think it could happen if a bean definition is added after a startable has been accessed.

}
}
return bean;
}

private void initializeStartables(Startable startableBean, String startableBeanName) {
List<String> beanNames = new ArrayList<>(
List.of(this.beanFactory.getBeanNamesForType(Startable.class, false, false)));
beanNames.remove(startableBeanName);
List<Object> beans = getBeans(beanNames);
if (beans == null) {
this.startablesInitialized.set(false);
return;
}
beanNames.add(startableBeanName);
beans.add(startableBean);
start(beans);
if (!beanNames.isEmpty()) {
logger.debug(LogMessage.format("Initialized and started startable beans '%s'", beanNames));
}
}

private void start(List<Object> beans) {
Set<Startable> startables = beans.stream()
.filter(Startable.class::isInstance)
.map(Startable.class::cast)
.collect(Collectors.toCollection(LinkedHashSet::new));
this.startup.start(startables);
}

private void initializeContainers() {
Set<String> beanNames = new LinkedHashSet<>();
beanNames.addAll(List.of(this.beanFactory.getBeanNamesForType(ContainerState.class, false, false)));
beanNames.addAll(List.of(this.beanFactory.getBeanNamesForType(Startable.class, false, false)));
initializeContainers(beanNames);
List<String> beanNames = List.of(this.beanFactory.getBeanNamesForType(ContainerState.class, false, false));
if (getBeans(beanNames) == null) {
this.containersInitialized.set(false);
}
}

private void initializeContainers(Set<String> beanNames) {
private List<Object> getBeans(List<String> beanNames) {
List<Object> beans = new ArrayList<>(beanNames.size());
for (String beanName : beanNames) {
try {
beans.add(this.beanFactory.getBean(beanName));
}
catch (BeanCreationException ex) {
if (ex.contains(BeanCurrentlyInCreationException.class)) {
return;
return null;
}
throw ex;
}
}
if (!this.containersInitialized) {
this.containersInitialized = true;
if (!beanNames.isEmpty()) {
logger.debug(LogMessage.format("Initialized container beans '%s'", beanNames));
}
start(beans);
}
}

private void start(List<Object> beans) {
Set<Startable> startables = beans.stream()
.filter(Startable.class::isInstance)
.map(Startable.class::cast)
.collect(Collectors.toCollection(LinkedHashSet::new));
this.startup.start(startables);
return beans;
}

@Override
Expand Down

0 comments on commit 0c66db7

Please sign in to comment.