Skip to content

Commit

Permalink
Check if profile file resources are in the location ClassLoader (#1215)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez authored Aug 28, 2024
1 parent 020130f commit 036e7e0
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ protected List<ConfigSource> tryClassPath(final URI uri, final int ordinal, fina
final List<ConfigSource> configSources = new ArrayList<>();
final ClassLoader useClassloader = classLoader != null ? classLoader : SecuritySupport.getContextClassLoader();
try {
consumeAsPaths(useClassloader, uri.getPath(), new ConfigSourcePathConsumer(ordinal, configSources));
consumeAsPaths(useClassloader, uri.getPath(),
new ConfigSourceClassPathConsumer(classLoader, uri, ordinal, configSources));
} catch (IOException e) {
throw ConfigMessages.msg.failedToLoadResource(e, uri.toString());
} catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -198,36 +199,11 @@ protected List<ConfigSource> tryHttpResource(final URI uri, final int ordinal) {
if (validExtension(uri.getPath())) {
ConfigSource mainSource = loadConfigSource(uri, ordinal);
configSources.add(mainSource);
configSources.addAll(tryProfiles(uri, mainSource));
configSources.add(profileConfigSourceFactory(uri, mainSource.getOrdinal()));
}
return configSources;
}

protected List<ConfigSource> tryProfiles(final URI uri, final ConfigSource mainSource) {
List<ConfigSource> configSources = new ArrayList<>();
configSources.add(new ConfigurableConfigSource(new ProfileConfigSourceFactory() {
@Override
public Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles) {
List<ConfigSource> profileSources = new ArrayList<>();
for (int i = profiles.size() - 1; i >= 0; i--) {
int ordinal = mainSource.getOrdinal() + profiles.size() - i;
for (String fileExtension : getFileExtensions()) {
URI profileUri = addProfileName(uri, profiles.get(i), fileExtension);
AbstractLocationConfigSourceLoader loader = AbstractLocationConfigSourceLoader.this;
profileSources.addAll(loader.loadProfileConfigSource(profileUri, ordinal));
}
}
return profileSources;
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(mainSource.getOrdinal());
}
}));
return configSources;
}

private static URL toURL(final URI uri) {
try {
return uri.toURL();
Expand Down Expand Up @@ -325,9 +301,41 @@ public URI convert(final String value) {
}
}

private class ConfigSourcePathConsumer implements Consumer<Path> {
private final List<ConfigSource> configSources;
private ConfigurableConfigSource profileConfigSourceFactory(final URI uri, final int ordinal) {
return new ConfigurableConfigSource(new ConfigurableProfileConfigSourceFactory(uri, ordinal));
}

protected final class ConfigurableProfileConfigSourceFactory implements ProfileConfigSourceFactory {
private final URI uri;
private final int ordinal;

public ConfigurableProfileConfigSourceFactory(final URI uri, final int ordinal) {
this.uri = uri;
this.ordinal = ordinal;
}

@Override
public Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles) {
List<ConfigSource> profileSources = new ArrayList<>();
for (int i = profiles.size() - 1; i >= 0; i--) {
int ordinal = this.ordinal + profiles.size() - i;
for (String fileExtension : getFileExtensions()) {
URI profileUri = addProfileName(uri, profiles.get(i), fileExtension);
profileSources.addAll(loadProfileConfigSource(profileUri, ordinal));
}
}
return profileSources;
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(ordinal);
}
}

protected final class ConfigSourcePathConsumer implements Consumer<Path> {
private final int ordinal;
private final List<ConfigSource> configSources;

public ConfigSourcePathConsumer(final int ordinal, final List<ConfigSource> configSources) {
this.ordinal = ordinal;
Expand All @@ -336,12 +344,64 @@ public ConfigSourcePathConsumer(final int ordinal, final List<ConfigSource> conf

@Override
public void accept(final Path path) {
final AbstractLocationConfigSourceLoader loader = AbstractLocationConfigSourceLoader.this;
AbstractLocationConfigSourceLoader loader = AbstractLocationConfigSourceLoader.this;
if (loader.validExtension(path.getFileName().toString())) {
ConfigSource mainSource = loader.loadConfigSource(path.toUri(), ordinal);
configSources.add(mainSource);
configSources.add(profileConfigSourceFactory(path.toUri(), mainSource.getOrdinal()));
}
}
}

protected final class ConfigSourceClassPathConsumer implements Consumer<Path> {
private final ClassLoader classLoader;
private final URI resource;

private final int ordinal;
private final List<ConfigSource> configSources;

public ConfigSourceClassPathConsumer(final ClassLoader classLoader, final URI resource, final int ordinal,
final List<ConfigSource> configSources) {
this.classLoader = classLoader;
this.resource = resource;
this.ordinal = ordinal;
this.configSources = configSources;
}

@Override
public void accept(final Path path) {
AbstractLocationConfigSourceLoader loader = AbstractLocationConfigSourceLoader.this;
if (loader.validExtension(path.getFileName().toString())) {
ConfigSource mainSource = loader.loadConfigSource(path.toUri(), ordinal);
configSources.add(mainSource);
configSources.addAll(loader.tryProfiles(path.toUri(), mainSource));
configSources.add(new ConfigurableConfigSource(new ProfileConfigSourceFactory() {
@Override
public Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles) {
List<ConfigSource> profileSources = new ArrayList<>();
for (int i = profiles.size() - 1; i >= 0; i--) {
int ordinal = mainSource.getOrdinal() + profiles.size() - i;
for (String fileExtension : getFileExtensions()) {
URI profileResource = addProfileName(resource, profiles.get(i), fileExtension);
URI profileUri = addProfileName(path.toUri(), profiles.get(i), fileExtension);
if ("jar".equals(profileUri.getScheme()) || isInClassloader(profileResource, profileUri)) {
profileSources.addAll(loader.loadProfileConfigSource(profileUri, ordinal));
}
}
}
return profileSources;
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(mainSource.getOrdinal());
}
}));
}
}

private boolean isInClassloader(final URI profileResource, final URI profileUri) {
return classLoader.resources(profileResource.getPath())
.anyMatch(url -> url.toString().equals(profileUri.toString()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.StreamSupport.stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.stream.Stream;
Expand Down Expand Up @@ -467,6 +471,65 @@ void mixedExtensions(@TempDir Path tempDir) throws Exception {
}
}

@Test
void profileFilesInClassloader(@TempDir Path tempDir) throws Exception {
Properties mainProperties = new Properties();
mainProperties.setProperty("my.prop.main", "main");
File mainFile = tempDir.resolve("application.properties").toFile();
try (FileOutputStream out = new FileOutputStream(mainFile)) {
mainProperties.store(out, null);
}

Properties testProperties = new Properties();
testProperties.setProperty("my.prop.test", "test");
File testFile = tempDir.resolve("application-test.properties").toFile();
try (FileOutputStream out = new FileOutputStream(testFile)) {
testProperties.store(out, null);
}

ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { tempDir.toUri().toURL() }, contextClassLoader) {
@Override
public URL getResource(final String name) {
if (name.endsWith("application-test.properties")) {
return null;
}
return super.getResource(name);
}

@Override
public Enumeration<URL> getResources(final String name) throws IOException {
if (name.endsWith("application-test.properties")) {
return Collections.emptyEnumeration();
}
return super.getResources(name);
}

@Override
public Stream<URL> resources(final String name) {
if (name.endsWith("application-test.properties")) {
return Stream.empty();
}
return super.resources(name);
}
}) {
Thread.currentThread().setContextClassLoader(urlClassLoader);

urlClassLoader.getResource("application.properties");

SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.addDefaultInterceptors()
.withProfile("test")
.build();

assertEquals("main", config.getRawValue("my.prop.main"));
assertNull(config.getRawValue("my.prop.test"));
} finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}

private static URLClassLoader urlClassLoader(ClassLoader parent, String... urls) {
return new URLClassLoader(Stream.of(urls).map(spec -> {
try {
Expand Down

0 comments on commit 036e7e0

Please sign in to comment.