Skip to content

Commit

Permalink
Merge pull request #34107 from aloubyansky/system-property-cl-config
Browse files Browse the repository at this point in the history
  • Loading branch information
aloubyansky authored Jun 19, 2023
2 parents 7b5daab + 21ad158 commit 4cc7319
Show file tree
Hide file tree
Showing 16 changed files with 419 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.quarkus.bootstrap.workspace.WorkspaceModule;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.maven.dependency.GACT;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.paths.PathCollection;

public class ConfiguredClassLoading implements Serializable {
Expand Down Expand Up @@ -61,6 +62,7 @@ public Builder setApplicationModel(ApplicationModel model) {

public ConfiguredClassLoading build() {

final String profilePrefix = getProfilePrefix(mode);
for (Path path : applicationRoot) {
Path props = path.resolve("application.properties");
if (Files.exists(props)) {
Expand All @@ -71,29 +73,19 @@ public ConfiguredClassLoading build() {
throw new RuntimeException("Failed to load bootstrap classloading config from application.properties",
e);
}
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.parent-first-artifacts", p, mode)),
parentFirstArtifacts);
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.reloadable-artifacts", p, mode)),
reloadableArtifacts);
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.removed-artifacts", p, mode)),
removedArtifacts);
collectRemovedResources("quarkus.class-loading.removed-resources.", p);

if (!flatTestClassPath && mode == Mode.TEST) {
final String s = p.getProperty(selectKey("quarkus.test.flat-class-path", p, mode));
if (s != null) {
flatTestClassPath = Boolean.parseBoolean(s);
}
}
readProperties(profilePrefix, p);
}
}

// this is to be able to support exclusion of artifacts from build classpath configured using system properties
readProperties(profilePrefix, System.getProperties());

if (appModel != null) {
appModel.getDependencies().forEach(d -> {
for (ResolvedDependency d : appModel.getDependencies()) {
if (d.isClassLoaderParentFirst()) {
parentFirstArtifacts.add(d.getKey());
}
});
}

if (mode == Mode.TEST) {
final WorkspaceModule module = appModel.getApplicationModule();
Expand Down Expand Up @@ -128,15 +120,31 @@ public ConfiguredClassLoading build() {
return ConfiguredClassLoading.this;
}

private void collectRemovedResources(String baseConfigKey, Properties properties) {
private void readProperties(String profilePrefix, Properties p) {
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.parent-first-artifacts", p, profilePrefix)),
parentFirstArtifacts);
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.reloadable-artifacts", p, profilePrefix)),
reloadableArtifacts);
collectArtifactKeys(p.getProperty(selectKey("quarkus.class-loading.removed-artifacts", p, profilePrefix)),
removedArtifacts);
collectRemovedResources("quarkus.class-loading.removed-resources.", p, profilePrefix);

if (!flatTestClassPath && mode == Mode.TEST) {
final String s = p.getProperty(selectKey("quarkus.test.flat-class-path", p, profilePrefix));
if (s != null) {
flatTestClassPath = Boolean.parseBoolean(s);
}
}
}

private void collectRemovedResources(String baseConfigKey, Properties properties, String profilePrefix) {
Properties profileProps = new Properties();
String profile = BootstrapProfile.getActiveProfile(mode);
for (Map.Entry<Object, Object> i : properties.entrySet()) {
String key = i.getKey().toString();
if (key.startsWith("%")) {
continue;
}
String profileKey = "%" + profile + "." + key;
final String profileKey = profilePrefix + key;
if (properties.containsKey(profileKey)) {
profileProps.put(key, properties.getProperty(profileKey));
} else {
Expand All @@ -160,15 +168,18 @@ private void collectRemovedResources(String baseConfigKey, Properties properties
}
}

private String selectKey(String base, Properties p, Mode mode) {
String profile = BootstrapProfile.getActiveProfile(mode);
String profileKey = "%" + profile + "." + base;
private String selectKey(String base, Properties p, String profilePrefix) {
final String profileKey = profilePrefix + base;
if (p.containsKey(profileKey)) {
return profileKey;
}
return base;
}

private String getProfilePrefix(Mode mode) {
return "%" + BootstrapProfile.getActiveProfile(mode) + ".";
}

private void collectArtifactKeys(String config, Collection<ArtifactKey> keys) {
if (config == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;

Expand Down Expand Up @@ -344,6 +345,120 @@ public void reaugmentationWithRemovedArtifacts() throws Exception {
}
}

@Test
@Timeout(80) // windows might need more time
public void reaugmentationWithRemovedArtifactsUsingSystemProperties() throws Exception {
File testDir = initProject("projects/extension-removed-resources",
"projects/extension-removed-artifacts-reaugmentation");
RunningInvoker running = new RunningInvoker(testDir, false);

// The default build
MavenProcessInvocationResult result = running
.execute(List.of("package", "-DskipTests", "-Dquarkus.package.type=mutable-jar"), Map.of());
await().atMost(1, TimeUnit.MINUTES).until(() -> result.getProcess() != null && !result.getProcess().isAlive());
assertThat(running.log()).containsIgnoringCase("BUILD SUCCESS");
running.stop();

testDir = testDir.toPath().resolve("runner").toFile();

Path runJar = testDir.toPath().toAbsolutePath().resolve(Paths.get("target/quarkus-app/quarkus-run.jar"));
assertThat(runJar).exists();

File output = new File(testDir, "target/output.log");
output.createNewFile();

Process process = doLaunch(runJar, output).start();
try {
AtomicReference<String> response = new AtomicReference<>();
await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/runtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("subatomic,supersonic");

await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/buildtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("subatomic,supersonic");
} finally {
process.destroy();
}

// re-augment and exclude html-extra
process = doLaunch(runJar, output,
List.of("-Dquarkus.class-loading.removed-artifacts=org.acme:acme-subatomic-provider",
"-Dquarkus.launch.rebuild=true"))
.start();
try {
assertThat(process.waitFor()).isEqualTo(0);
} finally {
process.destroy();
}

process = doLaunch(runJar, output).start();
try {
AtomicReference<String> response = new AtomicReference<>();
await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/runtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("supersonic");

await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/buildtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("supersonic");
} finally {
process.destroy();
}

// re-augment with the original dependencies
process = doLaunch(runJar, output, List.of("-Dquarkus.launch.rebuild=true")).start();
try {
assertThat(process.waitFor()).isEqualTo(0);
} finally {
process.destroy();
}

process = doLaunch(runJar, output).start();
try {
AtomicReference<String> response = new AtomicReference<>();
await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/runtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("subatomic,supersonic");

await()
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES).until(() -> {
String ret = DevModeTestUtils.getHttpResponse("/words/buildtime", false);
response.set(ret);
return true;
});
assertThat(response.get()).isEqualTo("subatomic,supersonic");
} finally {
process.destroy();
}
}

private void assertThatMutableFastJarWorks(String targetDirSuffix, String providersDir) throws Exception {
File testDir = initProject("projects/classic",
"projects/project-classic-console-output-mutable-fast-jar" + targetDirSuffix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

import java.util.Set;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.RemovedResourceBuildItem;
import io.quarkus.maven.dependency.ArtifactKey;

import org.acme.AcmeRecorder;
import org.acme.RecordedWords;
import org.acme.WordProvider;

class AcmeProcessor {

Expand All @@ -15,4 +22,16 @@ class AcmeProcessor {
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}

@BuildStep
void registerBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
additionalBeans.produce(AdditionalBeanBuildItem.builder()
.addBeanClasses(RecordedWords.class).build());
}

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void recordWords(AcmeRecorder recorder, BeanContainerBuildItem beanContainer) {
recorder.recordWords(beanContainer.getValue(), WordProvider.loadAndSortWords(), Thread.currentThread().getContextClassLoader().getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
<groupId>org.acme</groupId>
<artifactId>acme-resources</artifactId>
</dependency>
<dependency>
<groupId>org.acme</groupId>
<artifactId>acme-supersonic-provider</artifactId>
<version>\${project.version}</version>
</dependency>
<dependency>
<groupId>org.acme</groupId>
<artifactId>acme-subatomic-provider</artifactId>
<version>\${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.acme;

import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.runtime.annotations.Recorder;

import java.util.List;

@Recorder
public class AcmeRecorder {

public void recordWords(BeanContainer beanContainer, List<String> words, String classLoaderName) {
var bean = beanContainer.beanInstance(RecordedWords.class);
bean.setWords(words);
bean.setClassLoaderName(classLoaderName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.acme;

import jakarta.inject.Singleton;

import java.util.List;

@Singleton
public class RecordedWords {

private List<String> words;
private String classLoaderName;

void setWords(List<String> words) {
this.words = words;
}

public List<String> getWords() {
return words;
}

void setClassLoaderName(String classLoaderName) {
this.classLoaderName = classLoaderName;
}

public String getClassLoaderName() {
return classLoaderName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
</dependencies>
</dependencyManagement>
<modules>
<module>service-loader</module>
<module>supersonic-provider</module>
<module>subatomic-provider</module>
<module>resources</module>
<module>extension</module>
<module>runner</module>
Expand Down
Loading

0 comments on commit 4cc7319

Please sign in to comment.