Skip to content

ConnectionDetailsFactories should use the context class loader to load factories #45014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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 @@ -47,8 +47,26 @@ public class ConnectionDetailsFactories {

private final List<Registration<?, ?>> registrations = new ArrayList<>();

/**
* Create a new {@link ConnectionDetailsFactories} instance. This constructor uses the
* class loader of {@link ConnectionDetailsFactory} class to load the factories.
*/
public ConnectionDetailsFactories() {
this(SpringFactoriesLoader.forDefaultResourceLocation(ConnectionDetailsFactory.class.getClassLoader()));
this(false);
}

/**
* Create a new {@link ConnectionDetailsFactories} instance. This constructor takes a
* boolean argument to determine whether the context class loader should be used to
* load the factories. If {@code true} and the context class loader is available it
* will be used otherwise the class loader of {@link ConnectionDetailsFactory} class
* will be used.
* @param useContextClassLoader if {@code true} and the context class loader is
* available it will be used otherwise the class loader of
* {@link ConnectionDetailsFactory} class will be used.
*/
public ConnectionDetailsFactories(boolean useContextClassLoader) {
this(SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader(useContextClassLoader)));
}

@SuppressWarnings({ "rawtypes", "unchecked" })
Expand Down Expand Up @@ -107,6 +125,24 @@ public <S> Map<Class<?>, ConnectionDetails> getConnectionDetails(S source, boole
return List.copyOf(result);
}

/**
* Return the {@link ClassLoader} to use for loading factories.
* <p>
* The default implementation returns the context class loader of the current thread
* or the class loader of this class if the context class loader is {@code null}.
* @param useContextClassLoader if {@code true} and the context class loader is
* available it will be used otherwise the class loader of
* {@link ConnectionDetailsFactory} class will be used
* @return the class loader to use for loading factories
*/
private static ClassLoader getClassLoader(boolean useContextClassLoader) {
if (!useContextClassLoader) {
return ConnectionDetailsFactory.class.getClassLoader();
}
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
return (classLoader != null) ? classLoader : getClassLoader(false);
}

/**
* A {@link ConnectionDetailsFactory} registration.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class DockerComposeProperties {
*/
private boolean enabled = true;

/**
* Whether to try to use the context class loader for connection details factories.
*/
private boolean useContextClassLoader = false;

/**
* Arguments to pass to the Docker Compose command.
*/
Expand Down Expand Up @@ -89,10 +94,18 @@ public boolean isEnabled() {
return this.enabled;
}

public boolean isUseContextClassLoader() {
return this.useContextClassLoader;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public void setUseContextClassLoader(boolean useContextClassLoader) {
this.useContextClassLoader = useContextClassLoader;
}

public List<String> getArguments() {
return this.arguments;
}
Expand Down Expand Up @@ -137,7 +150,7 @@ public Readiness getReadiness() {
return this.readiness;
}

static DockerComposeProperties get(Binder binder) {
public static DockerComposeProperties get(Binder binder) {
return binder.bind(NAME, DockerComposeProperties.class).orElseGet(DockerComposeProperties::new);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import org.springframework.boot.autoconfigure.container.ContainerImageMetadata;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties;
import org.springframework.boot.docker.compose.lifecycle.DockerComposeServicesReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
Expand All @@ -46,32 +48,30 @@
class DockerComposeServiceConnectionsApplicationListener
implements ApplicationListener<DockerComposeServicesReadyEvent> {

private final ConnectionDetailsFactories factories;

DockerComposeServiceConnectionsApplicationListener() {
this(new ConnectionDetailsFactories());
}

DockerComposeServiceConnectionsApplicationListener(ConnectionDetailsFactories factories) {
this.factories = factories;
}

@Override
public void onApplicationEvent(DockerComposeServicesReadyEvent event) {
ApplicationContext applicationContext = event.getSource();
if (applicationContext instanceof BeanDefinitionRegistry registry) {
Binder binder = Binder.get(applicationContext.getEnvironment());
DockerComposeProperties properties = DockerComposeProperties.get(binder);
boolean useContextClassLoader = properties.isUseContextClassLoader();
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(useContextClassLoader);
Environment environment = applicationContext.getEnvironment();
registerConnectionDetails(registry, environment, event.getRunningServices());
registerConnectionDetails(registry, environment, event.getRunningServices(), factories);
}
}

private void registerConnectionDetails(BeanDefinitionRegistry registry, Environment environment,
List<RunningService> runningServices) {
List<RunningService> runningServices, ConnectionDetailsFactories factories) {
for (RunningService runningService : runningServices) {
DockerComposeConnectionSource source = new DockerComposeConnectionSource(runningService, environment);
this.factories.getConnectionDetails(source, false).forEach((connectionDetailsType, connectionDetails) -> {
factories.getConnectionDetails(source, false).forEach((connectionDetailsType, connectionDetails) -> {
register(registry, runningService, connectionDetailsType, connectionDetails);
this.factories.getConnectionDetails(connectionDetails, false)
factories.getConnectionDetails(connectionDetails, false)
.forEach((adaptedType, adaptedDetails) -> register(registry, runningService, adaptedType,
adaptedDetails));
});
Expand Down