Browse Source

Allow ConnectionDetailsFactories to use context class loader

Update `ConnectionDetailsFactories` so that the context classloader
can be used to load factories.

See gh-45014

Signed-off-by: lengors <24527258+lengors@users.noreply.github.com>
pull/45049/head
lengors 10 months ago committed by Phillip Webb
parent
commit
cc1e1cc92a
  1. 38
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java
  2. 15
      spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java
  3. 20
      spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java

38
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/service/connection/ConnectionDetailsFactories.java

@ -47,8 +47,26 @@ public class ConnectionDetailsFactories { @@ -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" })
@ -107,6 +125,24 @@ public class ConnectionDetailsFactories { @@ -107,6 +125,24 @@ public class ConnectionDetailsFactories {
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.
*

15
spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java

@ -46,6 +46,11 @@ public class DockerComposeProperties { @@ -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.
*/
@ -89,10 +94,18 @@ public class DockerComposeProperties { @@ -89,10 +94,18 @@ public class DockerComposeProperties {
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;
}
@ -137,7 +150,7 @@ public class DockerComposeProperties { @@ -137,7 +150,7 @@ public class DockerComposeProperties {
return this.readiness;
}
static DockerComposeProperties get(Binder binder) {
public static DockerComposeProperties get(Binder binder) {
return binder.bind(NAME, DockerComposeProperties.class).orElseGet(DockerComposeProperties::new);
}

20
spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java

@ -27,7 +27,9 @@ import org.springframework.beans.factory.support.RootBeanDefinition; @@ -27,7 +27,9 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
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;
@ -46,32 +48,30 @@ import org.springframework.util.StringUtils; @@ -46,32 +48,30 @@ import org.springframework.util.StringUtils;
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));
});

Loading…
Cancel
Save