From f7d0c2fc35b58b51b451de7e44e459e137a52045 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Wed, 25 May 2022 15:59:11 +0200 Subject: [PATCH] Support surefire plugin's classpathDependencyExcludes and additionalClasspathElements --- .../runner/bootstrap/StartupActionImpl.java | 2 +- .../io/quarkus/gradle/tasks/QuarkusDev.java | 16 +- .../main/java/io/quarkus/maven/DevMojo.java | 13 +- .../workspace/DefaultWorkspaceModule.java | 45 ++-- .../bootstrap/workspace/WorkspaceModule.java | 8 + .../bootstrap/app/ConfiguredClassLoading.java | 205 ++++++++++++++++-- .../bootstrap/app/CuratedApplication.java | 64 ++++-- .../bootstrap/app/QuarkusBootstrap.java | 123 ++--------- .../resolver/BootstrapAppModelResolver.java | 14 +- .../maven/BuildDependencyGraphVisitor.java | 7 +- .../DeploymentInjectingDependencyVisitor.java | 9 +- .../resolver/maven/MavenArtifactResolver.java | 7 +- .../maven/workspace/LocalProject.java | 66 ++++-- .../java/io/quarkus/maven/it/PackageIT.java | 9 + .../module-a/pom.xml | 30 +++ .../src/main/resources/module/module-a | 1 + .../module-b/pom.xml | 24 ++ .../src/main/resources/module/module-b | 1 + .../test-plugin-classpath-config/pom.xml | 51 +++++ .../extra-test-resources/module/module-extra | 1 + .../runner/pom.xml | 73 +++++++ .../main/java/org/acme/GreetingResource.java | 43 ++++ .../java/org/acme/GreetingResourceTest.java | 23 ++ 23 files changed, 631 insertions(+), 204 deletions(-) create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/src/main/resources/module/module-a create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/src/main/resources/module/module-b create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/extra-test-resources/module/module-extra create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/main/java/org/acme/GreetingResource.java create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/test/java/org/acme/GreetingResourceTest.java diff --git a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java index 42e7de07d7915..92ec553c6b5cf 100644 --- a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java +++ b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java @@ -59,7 +59,7 @@ public StartupActionImpl(CuratedApplication curatedApplication, BuildResult buil //test mode only has a single class loader, while dev uses a disposable runtime class loader //that is discarded between restarts Map resources = new HashMap<>(extractGeneratedResources(true)); - if (curatedApplication.getQuarkusBootstrap().isFlatClassPath()) { + if (curatedApplication.isFlatClassPath()) { resources.putAll(extractGeneratedResources(false)); baseClassLoader.reset(resources, transformedClasses); runtimeClassLoader = baseClassLoader; diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java index c87d690f71ff6..0a8849385e470 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java @@ -8,7 +8,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; -import java.util.Collections; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -45,6 +45,7 @@ import org.gradle.util.GradleVersion; import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.app.ConfiguredClassLoading; import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.devmode.DependenciesFilter; import io.quarkus.bootstrap.model.ApplicationModel; @@ -304,17 +305,18 @@ private QuarkusDevModeLauncher newLauncher() throws Exception { resourceDirs.add(resourceDir.getOutputDir()); } - Set configuredParentFirst = QuarkusBootstrap.createClassLoadingConfig(PathsCollection.from(resourceDirs), - QuarkusBootstrap.Mode.DEV, Collections.emptyList()).parentFirstArtifacts; - - Set parentFirstArtifactKeys = new HashSet<>(configuredParentFirst); - parentFirstArtifactKeys.addAll(appModel.getParentFirst()); + final Collection configuredParentFirst = ConfiguredClassLoading.builder() + .setApplicationModel(appModel) + .setApplicationRoot(PathsCollection.from(resourceDirs)) + .setMode(QuarkusBootstrap.Mode.DEV) + .addParentFirstArtifacts(appModel.getParentFirst()) + .build().getParentFirstArtifacts(); for (io.quarkus.maven.dependency.ResolvedDependency artifact : appModel.getDependencies()) { if (!projectDependencies.contains(artifact.getKey())) { artifact.getResolvedPaths().forEach(p -> { File file = p.toFile(); - if (file.exists() && parentFirstArtifactKeys.contains(artifact.getKey()) + if (file.exists() && configuredParentFirst.contains(artifact.getKey()) && filesIncludedInClasspath.add(file)) { getProject().getLogger().debug("Adding dependency {}", file); builder.classpathEntry(file); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index db0e5d6ffda36..6ca5e1869e4fb 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -80,6 +80,7 @@ import org.fusesource.jansi.internal.WindowsSupport; import io.quarkus.bootstrap.BootstrapConstants; +import io.quarkus.bootstrap.app.ConfiguredClassLoading; import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.devmode.DependenciesFilter; import io.quarkus.bootstrap.model.ApplicationModel; @@ -1075,21 +1076,23 @@ private QuarkusDevModeLauncher newLauncher() throws Exception { Path path = Paths.get(dir); resourceDirs.add(path); } - Set configuredParentFirst = QuarkusBootstrap.createClassLoadingConfig(PathsCollection.from(resourceDirs), - QuarkusBootstrap.Mode.DEV, Collections.emptyList()).parentFirstArtifacts; //in most cases these are not used, however they need to be present for some //parent-first cases such as logging //first we go through and get all the parent first artifacts - Set parentFirstArtifacts = new HashSet<>(configuredParentFirst); - parentFirstArtifacts.addAll(appModel.getParentFirst()); + final Collection configuredParentFirst = ConfiguredClassLoading.builder() + .setApplicationModel(appModel) + .setApplicationRoot(PathsCollection.from(resourceDirs)) + .setMode(QuarkusBootstrap.Mode.DEV) + .addParentFirstArtifacts(appModel.getParentFirst()) + .build().getParentFirstArtifacts(); for (Artifact appDep : project.getArtifacts()) { // only add the artifact if it's present in the dev mode context // we need this to avoid having jars on the classpath multiple times ArtifactKey key = ArtifactKey.gact(appDep.getGroupId(), appDep.getArtifactId(), appDep.getClassifier(), appDep.getArtifactHandler().getExtension()); - if (!builder.isLocal(key) && parentFirstArtifacts.contains(key)) { + if (!builder.isLocal(key) && configuredParentFirst.contains(key)) { builder.classpathEntry(appDep.getFile()); } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/DefaultWorkspaceModule.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/DefaultWorkspaceModule.java index 853eba5134974..63d07fd6ef810 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/DefaultWorkspaceModule.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/DefaultWorkspaceModule.java @@ -96,6 +96,18 @@ public boolean hasTestSources() { return DefaultWorkspaceModule.this.sourcesSets.containsKey(ArtifactSources.TEST); } + @Override + public Builder setTestClasspathDependencyExclusions(Collection excludes) { + DefaultWorkspaceModule.this.testClasspathDependencyExclusions = excludes; + return this; + } + + @Override + public Builder setAdditionalTestClasspathElements(Collection elements) { + DefaultWorkspaceModule.this.additionalTestClasspathElements = elements; + return this; + } + @Override public WorkspaceModule build() { final DefaultWorkspaceModule module = DefaultWorkspaceModule.this; @@ -154,6 +166,16 @@ public Collection getDirectDependencyConstraints() { public Collection getDirectDependencies() { return DefaultWorkspaceModule.this.getDirectDependencies(); } + + @Override + public Collection getTestClasspathDependencyExclusions() { + return DefaultWorkspaceModule.this.testClasspathDependencyExclusions; + } + + @Override + public Collection getAdditionalTestClasspathElements() { + return DefaultWorkspaceModule.this.additionalTestClasspathElements; + } } private WorkspaceModuleId id; @@ -163,6 +185,8 @@ public Collection getDirectDependencies() { private Map sourcesSets = new HashMap<>(); private List directDepConstraints; private List directDeps; + private Collection testClasspathDependencyExclusions = List.of(); + private Collection additionalTestClasspathElements = List.of(); private DefaultWorkspaceModule() { } @@ -202,10 +226,6 @@ public File getBuildDir() { return buildDir; } - public void addArtifactSources(ArtifactSources src) { - sourcesSets.put(src.getClassifier(), src); - } - @Override public boolean hasSources(String classifier) { return sourcesSets.containsKey(classifier); @@ -235,24 +255,19 @@ public Collection getDirectDependencyConstraints() { return directDepConstraints == null ? Collections.emptyList() : directDepConstraints; } - public void setDirectDependencyConstraints(List directDepConstraints) { - this.directDepConstraints = directDepConstraints; - } - @Override public Collection getDirectDependencies() { return directDeps == null ? Collections.emptyList() : directDeps; } - public void setDirectDependencies(List directDeps) { - this.directDeps = directDeps; + @Override + public Collection getTestClasspathDependencyExclusions() { + return testClasspathDependencyExclusions; } - public void addDirectDependency(Dependency directDep) { - if (directDeps == null || directDeps.isEmpty()) { - directDeps = new ArrayList<>(); - } - this.directDeps.add(directDep); + @Override + public Collection getAdditionalTestClasspathElements() { + return additionalTestClasspathElements; } @Override diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/WorkspaceModule.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/WorkspaceModule.java index 4e7ebda086f0d..304629d21a14e 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/WorkspaceModule.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/WorkspaceModule.java @@ -55,6 +55,10 @@ default PathTree getContentTree(String classifier) { Collection getDirectDependencies(); + Collection getTestClasspathDependencyExclusions(); + + Collection getAdditionalTestClasspathElements(); + Mutable mutable(); interface Mutable extends WorkspaceModule { @@ -77,6 +81,10 @@ interface Mutable extends WorkspaceModule { Mutable addArtifactSources(ArtifactSources sources); + Mutable setTestClasspathDependencyExclusions(Collection excludes); + + Mutable setAdditionalTestClasspathElements(Collection elements); + WorkspaceModule build(); default Mutable mutable() { diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/ConfiguredClassLoading.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/ConfiguredClassLoading.java index 868b3339b0bf7..86c710ab66d7c 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/ConfiguredClassLoading.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/ConfiguredClassLoading.java @@ -1,28 +1,205 @@ package io.quarkus.bootstrap.app; +import io.quarkus.bootstrap.app.QuarkusBootstrap.Mode; +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.GACT; +import io.quarkus.paths.PathCollection; +import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; public class ConfiguredClassLoading implements Serializable { private static final long serialVersionUID = 8458420778153976864L; - public final Set parentFirstArtifacts; - public final Set reloadableArtifacts; - public final Set removedArtifacts; - public final Map> removedResources; - public final boolean flatTestClassPath; - - public ConfiguredClassLoading(Set parentFirstArtifacts, Set reloadableArtifacts, - Set removedArtifacts, - Map> removedResources, boolean flatTestClassPath) { - this.parentFirstArtifacts = parentFirstArtifacts; - this.reloadableArtifacts = reloadableArtifacts; - this.removedResources = removedResources; - this.flatTestClassPath = flatTestClassPath; - this.removedArtifacts = removedArtifacts; + public class Builder { + + private PathCollection applicationRoot; + private Mode mode; + private ApplicationModel appModel; + + private Builder() { + } + + public Builder setApplicationRoot(PathCollection applicationRoot) { + this.applicationRoot = applicationRoot; + return this; + } + + public Builder setMode(Mode mode) { + this.mode = mode; + return this; + } + + public Builder setDefaultFlatTestClassPath(boolean flatClassPath) { + flatTestClassPath = flatClassPath; + return this; + } + + public Builder addParentFirstArtifacts(Collection parentFirst) { + parentFirstArtifacts.addAll(parentFirst); + return this; + } + + public Builder setApplicationModel(ApplicationModel model) { + this.appModel = model; + return this; + } + + public ConfiguredClassLoading build() { + + for (Path path : applicationRoot) { + Path props = path.resolve("application.properties"); + if (Files.exists(props)) { + final Properties p = new Properties(); + try (InputStream in = Files.newInputStream(props)) { + p.load(in); + } catch (IOException e) { + 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); + } + } + } + } + + if (appModel != null) { + parentFirstArtifacts.addAll(appModel.getParentFirst()); + + if (mode == Mode.TEST) { + final WorkspaceModule module = appModel.getApplicationModule(); + if (module != null) { + for (String s : module.getTestClasspathDependencyExclusions()) { + removedArtifacts.add(ArtifactKey.fromString(s)); + } + if (!module.getAdditionalTestClasspathElements().isEmpty()) { + additionalPaths = new ArrayList<>(module.getAdditionalTestClasspathElements().size()); + for (String s : module.getAdditionalTestClasspathElements()) { + final Path p = Path.of(s); + if (Files.exists(p)) { + additionalPaths.add(p); + } + } + } + } + } + } + + return ConfiguredClassLoading.this; + } + + private void collectRemovedResources(String baseConfigKey, Properties properties) { + Properties profileProps = new Properties(); + String profile = BootstrapProfile.getActiveProfile(mode); + for (Map.Entry i : properties.entrySet()) { + String key = i.getKey().toString(); + if (key.startsWith("%")) { + continue; + } + String profileKey = "%" + profile + "." + key; + if (properties.containsKey(profileKey)) { + profileProps.put(key, properties.getProperty(profileKey)); + } else { + profileProps.put(key, i.getValue()); + } + } + //now we have a 'sanitised' map with the correct props for the profile. + for (Map.Entry entry : profileProps.entrySet()) { + String key = entry.getKey().toString(); + String value = entry.getValue().toString(); + if (key.startsWith(baseConfigKey)) { + String artifactId = key.substring(baseConfigKey.length()); + artifactId = artifactId.replace("\"", ""); + List resources = Arrays.asList(value.split(",")); + removedResources.put(new GACT(artifactId.split(":")), resources); + } + } + } + + private String selectKey(String base, Properties p, Mode mode) { + String profile = BootstrapProfile.getActiveProfile(mode); + String profileKey = "%" + profile + "." + base; + if (p.containsKey(profileKey)) { + return profileKey; + } + return base; + } + + private void collectArtifactKeys(String config, Collection keys) { + if (config == null) { + return; + } + final String[] split = config.split(","); + for (String i : split) { + keys.add(new GACT(i.split(":"))); + } + } + } + + public static Builder builder() { + return new ConfiguredClassLoading().new Builder(); + } + + private final Set parentFirstArtifacts = new HashSet<>(); + private final Set reloadableArtifacts = new HashSet<>(); + private final Set removedArtifacts = new HashSet<>(); + private final Map> removedResources = new HashMap<>(); + private boolean flatTestClassPath; + private Collection additionalPaths = List.of(); + + private ConfiguredClassLoading() { + } + + public Collection getParentFirstArtifacts() { + return parentFirstArtifacts; + } + + public boolean isParentFirstArtifact(ArtifactKey key) { + return parentFirstArtifacts.contains(key); + } + + public boolean isReloadableArtifact(ArtifactKey key) { + return reloadableArtifacts.contains(key); + } + + public boolean isRemovedArtifact(ArtifactKey key) { + return removedArtifacts.contains(key); + } + + public Map> getRemovedResources() { + return removedResources; + } + + public boolean isFlatTestClassPath() { + return flatTestClassPath; + } + + public Collection getAdditionalClasspathElements() { + return additionalPaths; } } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java index d6b826942f491..7f60c010d39fd 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java @@ -16,6 +16,7 @@ import java.io.Serializable; import java.nio.file.Path; import java.security.ProtectionDomain; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -85,6 +86,10 @@ public AppModel getAppModel() { return BootstrapUtils.convert(appModel); } + public boolean isFlatClassPath() { + return configuredClassLoading.isFlatTestClassPath(); + } + public ApplicationModel getApplicationModel() { return appModel; } @@ -154,7 +159,7 @@ private synchronized void processCpElement(ResolvedDependency artifact, Consumer consumer.accept(ClassPathElement.EMPTY); return; } - List filteredResources = configuredClassLoading.removedResources.get(artifact.getKey()); + Collection filteredResources = configuredClassLoading.getRemovedResources().get(artifact.getKey()); if (filteredResources != null) { Consumer old = consumer; consumer = new Consumer() { @@ -181,8 +186,7 @@ public void accept(ClassPathElement classPathElement) { private void addCpElement(QuarkusClassLoader.Builder builder, ResolvedDependency dep, ClassPathElement element) { final ArtifactKey key = dep.getKey(); - if (appModel.getParentFirst().contains(key) - || configuredClassLoading.parentFirstArtifacts.contains(key)) { + if (configuredClassLoading.isParentFirstArtifact(key)) { //we always load this from the parent if it is available, as this acts as a bridge between the running //app and the dev mode code builder.addParentFirstElement(element); @@ -204,21 +208,21 @@ public synchronized QuarkusClassLoader getAugmentClassLoader() { //any of the runtime artifacts, or user classes //this will load any deployment artifacts from the parent CL if they are present for (ResolvedDependency i : appModel.getDependencies()) { - if (configuredClassLoading.reloadableArtifacts.contains(i.getKey())) { + if (configuredClassLoading.isRemovedArtifact(i.getKey())) { + processCpElement(i, builder::addBannedElement); continue; } - if (configuredClassLoading.removedArtifacts.contains(i.getKey())) { - processCpElement(i, builder::addBannedElement); - } else { - processCpElement(i, element -> addCpElement(builder, i, element)); + if (configuredClassLoading.isReloadableArtifact(i.getKey())) { + continue; } + processCpElement(i, element -> addCpElement(builder, i, element)); } for (Path i : quarkusBootstrap.getAdditionalDeploymentArchives()) { builder.addElement(ClassPathElement.fromPath(i, false)); } Map banned = new HashMap<>(); - for (List i : configuredClassLoading.removedResources.values()) { + for (Collection i : configuredClassLoading.getRemovedResources().values()) { for (String j : i) { banned.put(j, new byte[0]); } @@ -246,9 +250,7 @@ public synchronized QuarkusClassLoader getBaseRuntimeClassLoader() { .setAssertionsEnabled(quarkusBootstrap.isAssertionsEnabled()); builder.addClassLoaderEventListeners(quarkusBootstrap.getClassLoaderEventListeners()); - final boolean flatTestClassPath = quarkusBootstrap.getMode() == QuarkusBootstrap.Mode.TEST - && quarkusBootstrap.isFlatClassPath(); - if (flatTestClassPath) { + if (configuredClassLoading.isFlatTestClassPath()) { //in test mode we have everything in the base class loader //there is no need to restart so there is no need for an additional CL @@ -275,9 +277,14 @@ public synchronized QuarkusClassLoader getBaseRuntimeClassLoader() { } } } + for (Path i : configuredClassLoading.getAdditionalClasspathElements()) { + hotReloadPaths.add(i); + builder.addBannedElement(new ClassFilteredBannedElement(ClassPathElement.fromPath(i, true))); + } + builder.setResettableElement(new MemoryClassPathElement(Collections.emptyMap(), true)); Map banned = new HashMap<>(); - for (List i : configuredClassLoading.removedResources.values()) { + for (Collection i : configuredClassLoading.getRemovedResources().values()) { for (String j : i) { banned.put(j, new byte[0]); } @@ -285,19 +292,18 @@ public synchronized QuarkusClassLoader getBaseRuntimeClassLoader() { builder.addBannedElement(new MemoryClassPathElement(banned, true)); for (ResolvedDependency dependency : appModel.getDependencies()) { + if (configuredClassLoading.isRemovedArtifact(dependency.getKey())) { + processCpElement(dependency, builder::addBannedElement); + continue; + } if (!dependency.isRuntimeCp() || isHotReloadable(dependency, hotReloadPaths) - || configuredClassLoading.reloadableArtifacts.contains(dependency.getKey()) - || !flatTestClassPath && dependency.isReloadable() + || configuredClassLoading.isReloadableArtifact(dependency.getKey()) + || !configuredClassLoading.isFlatTestClassPath() && dependency.isReloadable() && appModel.getReloadableWorkspaceDependencies().contains(dependency.getKey())) { continue; } - - if (configuredClassLoading.removedArtifacts.contains(dependency.getKey())) { - processCpElement(dependency, builder::addBannedElement); - } else { - processCpElement(dependency, element -> addCpElement(builder, dependency, element)); - } + processCpElement(dependency, element -> addCpElement(builder, dependency, element)); } baseRuntimeClassLoader = builder.build(); @@ -336,12 +342,18 @@ public QuarkusClassLoader createDeploymentClassLoader() { } } for (ResolvedDependency dependency : appModel.getDependencies()) { + if (configuredClassLoading.isRemovedArtifact(dependency.getKey())) { + continue; + } if (dependency.isRuntimeCp() && dependency.isJar() && (dependency.isReloadable() && appModel.getReloadableWorkspaceDependencies().contains(dependency.getKey()) || - configuredClassLoading.reloadableArtifacts.contains(dependency.getKey()))) { + configuredClassLoading.isReloadableArtifact(dependency.getKey()))) { processCpElement(dependency, element -> addCpElement(builder, dependency, element)); } } + for (Path root : configuredClassLoading.getAdditionalClasspathElements()) { + builder.addElement(ClassPathElement.fromPath(root, true)); + } return builder.build(); } @@ -373,12 +385,18 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map addCpElement(builder, dependency, element)); } } + for (Path root : configuredClassLoading.getAdditionalClasspathElements()) { + builder.addElement(ClassPathElement.fromPath(root, true)); + } return builder.build(); } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java index 32e55ddb209d7..f617b5e9fba6d 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java @@ -9,23 +9,16 @@ import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.Dependency; -import io.quarkus.maven.dependency.GACT; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.paths.PathCollection; import io.quarkus.paths.PathList; -import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Set; @@ -93,11 +86,11 @@ public class QuarkusBootstrap implements Serializable { private final List classLoadListeners; private final boolean auxiliaryApplication; private final boolean hostApplicationIsTestOnly; - private final boolean flatClassPath; - private final ConfiguredClassLoading classLoadingConfig; private final boolean assertionsEnabled; + private final boolean defaultFlatTestClassPath; + private final Collection parentFirstArtifacts; - private QuarkusBootstrap(Builder builder, ConfiguredClassLoading classLoadingConfig) { + private QuarkusBootstrap(Builder builder) { this.applicationRoot = builder.applicationRoot; this.additionalApplicationArchives = new ArrayList<>(builder.additionalApplicationArchives); this.excludeFromClassPath = new ArrayList<>(builder.excludeFromClassPath); @@ -124,9 +117,9 @@ private QuarkusBootstrap(Builder builder, ConfiguredClassLoading classLoadingCon this.localArtifacts = new HashSet<>(builder.localArtifacts); this.classLoadListeners = builder.classLoadListeners; this.auxiliaryApplication = builder.auxiliaryApplication; - this.flatClassPath = builder.flatClassPath; - this.classLoadingConfig = classLoadingConfig; this.hostApplicationIsTestOnly = builder.hostApplicationIsTestOnly; + this.defaultFlatTestClassPath = builder.flatClassPath; + this.parentFirstArtifacts = builder.parentFirstArtifacts; } public CuratedApplication bootstrap() throws BootstrapException { @@ -134,6 +127,13 @@ public CuratedApplication bootstrap() throws BootstrapException { //once we have this it is up to augment to set up the class loader to actually use them if (existingModel != null) { + final ConfiguredClassLoading classLoadingConfig = ConfiguredClassLoading.builder() + .setApplicationRoot(applicationRoot) + .setDefaultFlatTestClassPath(defaultFlatTestClassPath) + .setMode(mode) + .addParentFirstArtifacts(parentFirstArtifacts) + .setApplicationModel(existingModel) + .build(); return new CuratedApplication(this, new CurationResult(existingModel), classLoadingConfig); } @@ -171,90 +171,16 @@ public CuratedApplication bootstrap() throws BootstrapException { } } + final ConfiguredClassLoading classLoadingConfig = ConfiguredClassLoading.builder() + .setApplicationRoot(applicationRoot) + .setDefaultFlatTestClassPath(defaultFlatTestClassPath) + .setMode(mode) + .addParentFirstArtifacts(parentFirstArtifacts) + .setApplicationModel(curationResult.getApplicationModel()) + .build(); return new CuratedApplication(this, curationResult, classLoadingConfig); } - public static ConfiguredClassLoading createClassLoadingConfig(PathCollection applicationRoot, Mode mode, - List parentFirstArtifacts) { - //look for an application.properties - for (Path path : applicationRoot) { - Path props = path.resolve("application.properties"); - if (Files.exists(props)) { - try (InputStream in = Files.newInputStream(props)) { - Properties p = new Properties(); - p.load(in); - Set parentFirst = toArtifactSet( - p.getProperty(selectKey("quarkus.class-loading.parent-first-artifacts", p, mode))); - parentFirst.addAll(parentFirstArtifacts); - Set liveReloadable = toArtifactSet( - p.getProperty(selectKey("quarkus.class-loading.reloadable-artifacts", p, mode))); - Set removedArtifacts = toArtifactSet( - p.getProperty(selectKey("quarkus.class-loading.removed-artifacts", p, mode))); - boolean flatClassPath = Boolean.parseBoolean( - p.getProperty(selectKey("quarkus.test.flat-class-path", p, mode))); - Map> removedResources = toArtifactMapList( - "quarkus.class-loading.removed-resources.", p, mode); - return new ConfiguredClassLoading(parentFirst, liveReloadable, removedArtifacts, removedResources, - flatClassPath); - } catch (IOException e) { - throw new RuntimeException("Failed to load bootstrap classloading config from application.properties", e); - } - } - } - return new ConfiguredClassLoading(new HashSet<>(parentFirstArtifacts), Collections.emptySet(), Collections.emptySet(), - Collections.emptyMap(), false); - } - - private static Map> toArtifactMapList(String baseConfigKey, Properties properties, Mode mode) { - Properties profileProps = new Properties(); - String profile = BootstrapProfile.getActiveProfile(mode); - for (Map.Entry i : properties.entrySet()) { - String key = i.getKey().toString(); - if (key.startsWith("%")) { - continue; - } - String profileKey = "%" + profile + "." + key; - if (properties.containsKey(profileKey)) { - profileProps.put(key, properties.getProperty(profileKey)); - } else { - profileProps.put(key, i.getValue()); - } - } - //now we have a 'sanitised' map with the correct props for the profile. - Map> ret = new HashMap<>(); - for (Map.Entry entry : profileProps.entrySet()) { - String key = entry.getKey().toString(); - String value = entry.getValue().toString(); - if (key.startsWith(baseConfigKey)) { - String artifactId = key.substring(baseConfigKey.length()); - artifactId = artifactId.replace("\"", ""); - List resources = Arrays.asList(value.split(",")); - ret.put(new GACT(artifactId.split(":")), resources); - } - } - return ret; - } - - private static String selectKey(String base, Properties p, Mode mode) { - String profile = BootstrapProfile.getActiveProfile(mode); - String profileKey = "%" + profile + "." + base; - if (p.containsKey(profileKey)) { - return profileKey; - } - return base; - } - - private static Set toArtifactSet(String config) { - if (config == null) { - return new HashSet<>(); - } - Set ret = new HashSet<>(); - for (String i : config.split(",")) { - ret.add(new GACT(i.split(":"))); - } - return ret; - } - public AppModelResolver getAppModelResolver() { return appModelResolver; } @@ -366,10 +292,6 @@ public Builder clonedBuilder() { return builder; } - public boolean isFlatClassPath() { - return flatClassPath; - } - public boolean isTest() { return test; } @@ -615,12 +537,7 @@ public QuarkusBootstrap build() { if (appArtifact != null) { localArtifacts.add(appArtifact.getKey()); } - - ConfiguredClassLoading classLoadingConfig = createClassLoadingConfig(applicationRoot, mode, parentFirstArtifacts); - if (classLoadingConfig.flatTestClassPath && mode == Mode.TEST) { - flatClassPath = true; - } - return new QuarkusBootstrap(this, classLoadingConfig); + return new QuarkusBootstrap(this); } } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java index fd0475a08117a..0c007e6d106a1 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/BootstrapAppModelResolver.java @@ -269,17 +269,17 @@ private ApplicationModel doResolveModel(ArtifactCoords coords, final ResolvedDependency appArtifact = resolve(coords, mvnArtifact, managedRepos); - final List excludedScopes; + final String[] excludedScopes; if (test) { - excludedScopes = List.of(); + excludedScopes = new String[0]; } else if (devmode) { - excludedScopes = List.of("test"); + excludedScopes = new String[] { "test" }; } else { - excludedScopes = List.of("provided", "test"); + excludedScopes = new String[] { "provided", "test" }; } final DependencyNode resolvedDeps = mvn.resolveManagedDependencies(mvnArtifact, - directMvnDeps, managedDeps, managedRepos, excludedScopes.toArray(new String[0])).getRoot(); + directMvnDeps, managedDeps, managedRepos, excludedScopes).getRoot(); ArtifactDescriptorResult appArtifactDescr = mvn.resolveDescriptor(toAetherArtifact(appArtifact)); if (managingProject == null) { @@ -307,7 +307,7 @@ private ApplicationModel doResolveModel(ArtifactCoords coords, } private ApplicationModel buildAppModel(ResolvedDependency appArtifact, DependencyNode resolvedDeps, - Set reloadableModules, List managedDeps, final List repos) + Set reloadableModules, List managedDeps, List repos) throws AppModelResolverException, BootstrapMavenException { final ApplicationModelBuilder appBuilder = new ApplicationModelBuilder().setAppArtifact(appArtifact); if (appArtifact.getWorkspaceModule() != null) { @@ -340,7 +340,7 @@ private ApplicationModel buildAppModel(ResolvedDependency appArtifact, Dependenc throw new AppModelResolverException("Failed to normalize the dependency graph", e); } final BuildDependencyGraphVisitor buildDepsVisitor = new BuildDependencyGraphVisitor( - deploymentInjector.allRuntimeDeps, + deploymentInjector.getAllRuntimeDependencies(), buildTreeConsumer); buildDepsVisitor.visit(resolvedDeps); final List requests = buildDepsVisitor.getArtifactRequests(); diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java index c3bfea2fd193f..4126ddf36d6a0 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BuildDependencyGraphVisitor.java @@ -143,8 +143,7 @@ private void visitLeave(DependencyNode node) { requests.add(new ArtifactRequest(node)); } if (currentDeployment != null) { - if (currentRuntime == null && !allRuntimeDeps.contains(new GACT(artifact.getGroupId(), artifact.getArtifactId(), - artifact.getClassifier(), artifact.getExtension()))) { + if (currentRuntime == null && !allRuntimeDeps.contains(getKey(artifact))) { deploymentDepNodes.add(node); } else if (currentRuntime == node) { currentRuntime = null; @@ -155,4 +154,8 @@ private void visitLeave(DependencyNode node) { } } } + + private static ArtifactKey getKey(Artifact artifact) { + return new GACT(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getExtension()); + } } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java index 3f7f75e08f12c..e46d297af97ae 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/DeploymentInjectingDependencyVisitor.java @@ -81,12 +81,13 @@ public static Artifact getRuntimeArtifact(DependencyNode dep) { private List conditionalDepsToProcess = new ArrayList<>(); private final Deque> exclusionStack = new ArrayDeque<>(); - public final Set allRuntimeDeps = new HashSet<>(); + private final Set allRuntimeDeps = new HashSet<>(); public DeploymentInjectingDependencyVisitor(MavenArtifactResolver resolver, List managedDeps, List mainRepos, ApplicationModelBuilder appBuilder, boolean collectReloadableModules) throws BootstrapDependencyProcessingException { + if (collectReloadableModules) { setWalkingFlag(COLLECT_RELOADABLE_MODULES); } @@ -113,6 +114,10 @@ public DeploymentInjectingDependencyVisitor(MavenArtifactResolver resolver, List this.appBuilder = appBuilder; } + public Set getAllRuntimeDependencies() { + return allRuntimeDeps; + } + public boolean isInjectedDeps() { return !topExtensionDeps.isEmpty(); } @@ -647,7 +652,7 @@ private static boolean isSameKey(Artifact a1, Artifact a2) { && a2.getExtension().equals(a1.getExtension()); } - public static GACT getKey(Artifact a) { + public static ArtifactKey getKey(Artifact a) { return new GACT(a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getExtension()); } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/MavenArtifactResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/MavenArtifactResolver.java index 19739c4be07d0..47eba2f1ebeaf 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/MavenArtifactResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/MavenArtifactResolver.java @@ -273,7 +273,7 @@ public DependencyResult resolveManagedDependencies(Artifact artifact, List deps, List managedDeps, - List mainRepos, String... excludedScopes) throws BootstrapMavenException { - return newCollectManagedRequest(artifact, deps, managedDeps, mainRepos, Collections.emptyList(), excludedScopes); - } - private CollectRequest newCollectManagedRequest(Artifact artifact, List deps, List managedDeps, List mainRepos, Collection exclusions, String... excludedScopes) throws BootstrapMavenException { diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/workspace/LocalProject.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/workspace/LocalProject.java index 86da66217f991..7679c197e3edd 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/workspace/LocalProject.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/workspace/LocalProject.java @@ -342,30 +342,58 @@ public WorkspaceModule toWorkspaceModule() { boolean addDefaultSourceSet = true; if (build != null && !build.getPlugins().isEmpty()) { for (Plugin plugin : build.getPlugins()) { - if (!plugin.getArtifactId().equals("maven-jar-plugin")) { - continue; - } - if (plugin.getExecutions().isEmpty()) { - final DefaultArtifactSources src = processJarPluginExecutionConfig(plugin.getConfiguration(), false); - if (src != null) { - addDefaultSourceSet = false; - moduleBuilder.addArtifactSources(src); - } - } else { - for (PluginExecution e : plugin.getExecutions()) { - DefaultArtifactSources src = null; - if (e.getGoals().contains(ArtifactCoords.TYPE_JAR)) { - src = processJarPluginExecutionConfig(e.getConfiguration(), false); - addDefaultSourceSet &= !e.getId().equals("default-jar"); - } else if (e.getGoals().contains("test-jar")) { - src = processJarPluginExecutionConfig(e.getConfiguration(), true); - } + if (plugin.getArtifactId().equals("maven-jar-plugin")) { + if (plugin.getExecutions().isEmpty()) { + final DefaultArtifactSources src = processJarPluginExecutionConfig(plugin.getConfiguration(), false); if (src != null) { + addDefaultSourceSet = false; moduleBuilder.addArtifactSources(src); } + } else { + for (PluginExecution e : plugin.getExecutions()) { + DefaultArtifactSources src = null; + if (e.getGoals().contains(ArtifactCoords.TYPE_JAR)) { + src = processJarPluginExecutionConfig(e.getConfiguration(), false); + addDefaultSourceSet &= !e.getId().equals("default-jar"); + } else if (e.getGoals().contains("test-jar")) { + src = processJarPluginExecutionConfig(e.getConfiguration(), true); + } + if (src != null) { + moduleBuilder.addArtifactSources(src); + } + } + } + } else if (plugin.getArtifactId().equals("maven-surefire-plugin") && plugin.getConfiguration() != null) { + Object config = plugin.getConfiguration(); + if (config == null || !(config instanceof Xpp3Dom)) { + continue; + } + Xpp3Dom dom = (Xpp3Dom) config; + final Xpp3Dom depExcludes = dom.getChild("classpathDependencyExcludes"); + if (depExcludes != null) { + final Xpp3Dom[] excludes = depExcludes.getChildren("classpathDependencyExclude"); + if (excludes != null) { + final List list = new ArrayList<>(excludes.length); + for (Xpp3Dom exclude : excludes) { + list.add(exclude.getValue()); + } + moduleBuilder.setTestClasspathDependencyExclusions(list); + } + } + final Xpp3Dom additionalElements = dom.getChild("additionalClasspathElements"); + if (additionalElements != null) { + final Xpp3Dom[] elements = additionalElements.getChildren("additionalClasspathElement"); + if (elements != null) { + final List list = new ArrayList<>(elements.length); + for (Xpp3Dom element : elements) { + for (String s : element.getValue().split(",")) { + list.add(stripProjectBasedirPrefix(s, PROJECT_BASEDIR)); + } + } + moduleBuilder.setAdditionalTestClasspathElements(list); + } } } - break; } } diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java index 567b0669a9e3a..a4edd93af7f5b 100644 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java +++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java @@ -33,6 +33,15 @@ public class PackageIT extends MojoTestBase { private RunningInvoker running; private File testDir; + @Test + public void testPluginClasspathConfig() throws Exception { + testDir = initProject("projects/test-plugin-classpath-config"); + running = new RunningInvoker(testDir, false); + final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"), + Collections.emptyMap()); + assertThat(result.getProcess().waitFor()).isEqualTo(0); + } + @Test public void testUberJarMavenPluginConfiguration() throws MavenInvocationException, IOException, InterruptedException { diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml new file mode 100644 index 0000000000000..068c46c98c59b --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.acme + code-with-quarkus + 1.0.0-SNAPSHOT + + acme-module-a + + + org.acme + acme-module-b + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/src/main/resources/module/module-a b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/src/main/resources/module/module-a new file mode 100644 index 0000000000000..7b8b9c8487edb --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/src/main/resources/module/module-a @@ -0,0 +1 @@ +module-a \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml new file mode 100644 index 0000000000000..f2f049d720606 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.acme + code-with-quarkus + 1.0.0-SNAPSHOT + + acme-module-b + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/src/main/resources/module/module-b b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/src/main/resources/module/module-b new file mode 100644 index 0000000000000..c18feaa97e8a5 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/src/main/resources/module/module-b @@ -0,0 +1 @@ +module-b \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/pom.xml new file mode 100644 index 0000000000000..333e129399de8 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + org.acme + code-with-quarkus + 1.0.0-SNAPSHOT + pom + + 3.8.1 + false + 11 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus + @project.version@ + 3.0.0-M5 + + + + + \${quarkus.platform.group-id} + \${quarkus.platform.artifact-id} + \${quarkus.platform.version} + pom + import + + + org.acme + acme-runner + 1.0.0-SNAPSHOT + + + org.acme + acme-module-a + 1.0.0-SNAPSHOT + + + org.acme + acme-module-b + 1.0.0-SNAPSHOT + + + + + runner + module-a + module-b + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/extra-test-resources/module/module-extra b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/extra-test-resources/module/module-extra new file mode 100644 index 0000000000000..ec33801adf0b2 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/extra-test-resources/module/module-extra @@ -0,0 +1 @@ +module-extra \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml new file mode 100644 index 0000000000000..329bf7effd53e --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.acme + code-with-quarkus + 1.0.0-SNAPSHOT + + acme-runner + + + org.acme + acme-module-a + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + \${quarkus.platform.group-id} + quarkus-maven-plugin + \${quarkus.platform.version} + true + + + + build + + + + + + maven-compiler-plugin + \${compiler-plugin.version} + + + -parameters + + + + + maven-surefire-plugin + \${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + \${maven.home} + + + org.acme:acme-module-a + + + \${project.basedir}/extra-test-resources + + + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/main/java/org/acme/GreetingResource.java b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/main/java/org/acme/GreetingResource.java new file mode 100644 index 0000000000000..774b1a8b4307d --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/main/java/org/acme/GreetingResource.java @@ -0,0 +1,43 @@ +package org.acme; + +import java.net.URL; +import java.util.Enumeration; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/hello") +public class GreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + final Enumeration en; + try { + en = Thread.currentThread().getContextClassLoader().getResources("module"); + } catch(java.io.IOException e) { + throw new IllegalStateException("Failed to locate 'module' resources on the classpath", e); + } + if(!en.hasMoreElements()) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + sb.append(getNextModuleName(en)); + while(en.hasMoreElements()) { + sb.append(',').append(getNextModuleName(en)); + } + return sb.toString(); + } + + private String getNextModuleName(final Enumeration en) { + String s = en.nextElement().toExternalForm(); + s = s.substring(0, s.length() - "module".length() - 1); + if(s.endsWith("target/classes")) { + s = s.substring(0, s.length() - "target/classes".length() - 1); + } + s = s.substring(s.lastIndexOf('/') + 1); + return s; + } +} \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/test/java/org/acme/GreetingResourceTest.java b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/test/java/org/acme/GreetingResourceTest.java new file mode 100644 index 0000000000000..f5cd9f2580cfb --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/src/test/java/org/acme/GreetingResourceTest.java @@ -0,0 +1,23 @@ +package org.acme; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; + +@QuarkusTest +public class GreetingResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(containsString("module-b"), + containsString("extra-test-resources"), + not(containsString("module-a"))); + } +} \ No newline at end of file