Skip to content

Commit a29a09b

Browse files
aloubyanskygsmet
authored andcommitted
Make sure extension runtime artifacts are not flagged as reloadable in Gradle projects
(cherry picked from commit 1b0b8dd)
1 parent 07df9e8 commit a29a09b

File tree

7 files changed

+221
-86
lines changed

7 files changed

+221
-86
lines changed

devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusApplicationModelTask.java

Lines changed: 61 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ private static void fileDependenciesExtractor(ApplicationModelBuilder modelBuild
242242
.setDirect(true)
243243
.setRuntimeCp()
244244
.setDeploymentCp();
245-
Utils.processQuarkusDependency(artifactBuilder, modelBuilder);
245+
processQuarkusDependency(artifactBuilder, modelBuilder);
246246
modelBuilder.addDependency(artifactBuilder);
247247
}
248248
}
@@ -308,22 +308,21 @@ private static void collectDependencies(
308308
parentModule.addDependency(new ArtifactDependency(depCoords));
309309
}
310310

311-
if (Utils.processQuarkusDependency(depBuilder, modelBuilder)) {
311+
if (processQuarkusDependency(depBuilder, modelBuilder)) {
312312
if (isFlagOn(flags, COLLECT_TOP_EXTENSION_RUNTIME_NODES)) {
313313
depBuilder.setFlags(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT);
314314
newFlags = clearFlag(newFlags, COLLECT_TOP_EXTENSION_RUNTIME_NODES);
315315
}
316-
newFlags = clearFlag(newFlags, COLLECT_RELOADABLE_MODULES);
317316
}
318317
if (isFlagOn(flags, COLLECT_RELOADABLE_MODULES)) {
319-
// Checking whether current dependency is a project module is a temporary workaround,
320-
// that is required while projectModule for project dependencies is null (current
321-
// deficiency of this task).
322-
// That's why we set the workspace module flag explicitly via setWorkspaceModule().
323-
// Once we have projectModule set for project dependencies, we can remove this workaround.
324-
final boolean isProjectDependency = resolvedDependency.getSelected()
325-
.getId() instanceof ProjectComponentIdentifier;
326-
if (projectModule != null || isProjectDependency) {
318+
if (!depBuilder.isRuntimeExtensionArtifact()
319+
&& (projectModule != null
320+
// Checking whether current dependency is a project module is a temporary workaround,
321+
// that is required while projectModule for project dependencies is null (current
322+
// deficiency of this task).
323+
// That's why we set the workspace module flag explicitly via setWorkspaceModule().
324+
// Once we have projectModule set for project dependencies, we can remove this workaround.
325+
|| resolvedDependency.getSelected().getId() instanceof ProjectComponentIdentifier)) {
327326
depBuilder.setReloadable().setWorkspaceModule();
328327
modelBuilder.addReloadableWorkspaceModule(artifactKey);
329328
} else {
@@ -552,66 +551,63 @@ public File getFile() {
552551
}
553552
}
554553

555-
public static class Utils {
556-
557-
public static boolean processQuarkusDependency(ResolvedDependencyBuilder artifactBuilder,
558-
ApplicationModelBuilder modelBuilder) {
559-
for (Path artifactPath : artifactBuilder.getResolvedPaths()) {
560-
if (!Files.exists(artifactPath) || !artifactBuilder.getType().equals(ArtifactCoords.TYPE_JAR)) {
561-
break;
562-
}
563-
if (Files.isDirectory(artifactPath)) {
564-
return processQuarkusDir(artifactBuilder, artifactPath.resolve(BootstrapConstants.META_INF), modelBuilder);
565-
} else {
566-
try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactPath)) {
567-
return processQuarkusDir(artifactBuilder, artifactFs.getPath(BootstrapConstants.META_INF),
568-
modelBuilder);
569-
} catch (IOException e) {
570-
throw new RuntimeException("Failed to process " + artifactPath, e);
571-
}
554+
private static boolean processQuarkusDependency(ResolvedDependencyBuilder artifactBuilder,
555+
ApplicationModelBuilder modelBuilder) {
556+
for (Path artifactPath : artifactBuilder.getResolvedPaths()) {
557+
if (!Files.exists(artifactPath) || !artifactBuilder.getType().equals(ArtifactCoords.TYPE_JAR)) {
558+
break;
559+
}
560+
if (Files.isDirectory(artifactPath)) {
561+
return processQuarkusDir(artifactBuilder, artifactPath.resolve(BootstrapConstants.META_INF), modelBuilder);
562+
} else {
563+
try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactPath)) {
564+
return processQuarkusDir(artifactBuilder, artifactFs.getPath(BootstrapConstants.META_INF),
565+
modelBuilder);
566+
} catch (IOException e) {
567+
throw new RuntimeException("Failed to process " + artifactPath, e);
572568
}
573569
}
574-
return false;
575570
}
571+
return false;
572+
}
576573

577-
private static boolean processQuarkusDir(ResolvedDependencyBuilder artifactBuilder, Path quarkusDir,
578-
ApplicationModelBuilder modelBuilder) {
579-
if (!Files.exists(quarkusDir)) {
580-
return false;
581-
}
582-
final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
583-
if (!Files.exists(quarkusDescr)) {
584-
return false;
585-
}
586-
final Properties extProps = readDescriptor(quarkusDescr);
587-
if (extProps == null) {
588-
return false;
589-
}
590-
artifactBuilder.setRuntimeExtensionArtifact();
591-
modelBuilder.handleExtensionProperties(extProps, artifactBuilder.getKey());
592-
593-
final String providesCapabilities = extProps.getProperty(BootstrapConstants.PROP_PROVIDES_CAPABILITIES);
594-
if (providesCapabilities != null) {
595-
modelBuilder
596-
.addExtensionCapabilities(
597-
CapabilityContract.of(artifactBuilder.toGACTVString(), providesCapabilities, null));
598-
}
599-
return true;
574+
private static boolean processQuarkusDir(ResolvedDependencyBuilder artifactBuilder, Path quarkusDir,
575+
ApplicationModelBuilder modelBuilder) {
576+
if (!Files.exists(quarkusDir)) {
577+
return false;
578+
}
579+
final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
580+
if (!Files.exists(quarkusDescr)) {
581+
return false;
582+
}
583+
final Properties extProps = readDescriptor(quarkusDescr);
584+
if (extProps == null) {
585+
return false;
600586
}
587+
artifactBuilder.setRuntimeExtensionArtifact();
588+
modelBuilder.handleExtensionProperties(extProps, artifactBuilder.getKey());
589+
590+
final String providesCapabilities = extProps.getProperty(BootstrapConstants.PROP_PROVIDES_CAPABILITIES);
591+
if (providesCapabilities != null) {
592+
modelBuilder
593+
.addExtensionCapabilities(
594+
CapabilityContract.of(artifactBuilder.toGACTVString(), providesCapabilities, null));
595+
}
596+
return true;
597+
}
601598

602-
private static Properties readDescriptor(final Path path) {
603-
final Properties rtProps;
604-
if (!Files.exists(path)) {
605-
// not a platform artifact
606-
return null;
607-
}
608-
rtProps = new Properties();
609-
try (BufferedReader reader = Files.newBufferedReader(path)) {
610-
rtProps.load(reader);
611-
} catch (IOException e) {
612-
throw new UncheckedIOException("Failed to load extension description " + path, e);
613-
}
614-
return rtProps;
599+
private static Properties readDescriptor(final Path path) {
600+
final Properties rtProps;
601+
if (!Files.exists(path)) {
602+
// not a platform artifact
603+
return null;
604+
}
605+
rtProps = new Properties();
606+
try (BufferedReader reader = Files.newBufferedReader(path)) {
607+
rtProps.load(reader);
608+
} catch (IOException e) {
609+
throw new UncheckedIOException("Failed to load extension description " + path, e);
615610
}
611+
return rtProps;
616612
}
617613
}

devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ExtensionDescriptorTask.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
3737

3838
import io.quarkus.bootstrap.BootstrapConstants;
39-
import io.quarkus.bootstrap.model.AppArtifactCoords;
4039
import io.quarkus.bootstrap.model.AppArtifactKey;
4140
import io.quarkus.bootstrap.model.ApplicationModelBuilder;
4241
import io.quarkus.devtools.project.extensions.ScmInfoProvider;
@@ -322,11 +321,9 @@ private void computeArtifactCoords(ObjectNode extObject) {
322321
}
323322
}
324323
if (artifactNode == null || groupId == null || artifactId == null || version == null) {
325-
final AppArtifactCoords coords = new AppArtifactCoords(
324+
final ArtifactCoords coords = ArtifactCoords.jar(
326325
groupId == null ? projectInfo.get("group") : groupId,
327326
artifactId == null ? projectInfo.get("name") : artifactId,
328-
null,
329-
"jar",
330327
version == null ? projectInfo.get("version") : version);
331328
extObject.put("artifact", coords.toString());
332329
}

independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/BootstrapUtils.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,38 @@ public static ApplicationModel readAppModelWithWorkspaceId(Path file, int worksp
175175
}
176176
return null;
177177
}
178+
179+
/**
180+
* Generates a comma-separated list of flag names for an integer representation of the flags.
181+
*
182+
* @param flags flags as an integer value
183+
* @return comma-separated list of the flag names
184+
*/
185+
public static String toTextFlags(int flags) {
186+
var sb = new StringBuilder();
187+
appendFlagIfSet(sb, flags, DependencyFlags.OPTIONAL, "optional");
188+
appendFlagIfSet(sb, flags, DependencyFlags.DIRECT, "direct");
189+
appendFlagIfSet(sb, flags, DependencyFlags.RUNTIME_CP, "runtime-cp");
190+
appendFlagIfSet(sb, flags, DependencyFlags.DEPLOYMENT_CP, "deployment-cp");
191+
appendFlagIfSet(sb, flags, DependencyFlags.RUNTIME_EXTENSION_ARTIFACT, "runtime-extension-artifact");
192+
appendFlagIfSet(sb, flags, DependencyFlags.WORKSPACE_MODULE, "workspace-module");
193+
appendFlagIfSet(sb, flags, DependencyFlags.RELOADABLE, "reloadable");
194+
appendFlagIfSet(sb, flags, DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT,
195+
"top-level-runtime-extension-artifact");
196+
appendFlagIfSet(sb, flags, DependencyFlags.CLASSLOADER_PARENT_FIRST, "classloader-parent-first");
197+
appendFlagIfSet(sb, flags, DependencyFlags.CLASSLOADER_RUNNER_PARENT_FIRST, "classloader-runner-parent-first");
198+
appendFlagIfSet(sb, flags, DependencyFlags.CLASSLOADER_LESSER_PRIORITY, "classloader-lesser-priority");
199+
appendFlagIfSet(sb, flags, DependencyFlags.COMPILE_ONLY, "compile-only");
200+
appendFlagIfSet(sb, flags, DependencyFlags.VISITED, "visited");
201+
return sb.toString();
202+
}
203+
204+
private static void appendFlagIfSet(StringBuilder sb, int flags, int flagValue, String flagName) {
205+
if ((flags & flagValue) == flagValue) {
206+
if (!sb.isEmpty()) {
207+
sb.append(", ");
208+
}
209+
sb.append(flagName);
210+
}
211+
}
178212
}

integration-tests/gradle/src/main/resources/multi-composite-build-extensions-project/application/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ repositories {
1616

1717
dependencies {
1818
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
19-
testImplementation 'org.junit.jupiter:junit-jupiter-api'
20-
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
2119
implementation 'io.quarkus:quarkus-resteasy'
2220
implementation ('org.acme.libs:libraryB')
2321
implementation ('org.acme.libs:libraryA')
2422
implementation ('org.acme.extensions:another-example-extension')
2523

24+
testImplementation 'io.quarkus:quarkus-junit5'
25+
testImplementation 'io.rest-assured:rest-assured'
2626
}
2727

2828
test {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.acme.quarkus.sample;
2+
3+
import io.quarkus.test.junit.QuarkusTest;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static io.restassured.RestAssured.given;
7+
import static org.hamcrest.CoreMatchers.is;
8+
9+
@QuarkusTest
10+
class GreetingResourceTest {
11+
@Test
12+
void testHelloEndpoint() {
13+
given()
14+
.when().get("/hello")
15+
.then()
16+
.statusCode(200)
17+
.body(is("hello from LibB and LibA extension enabled: false"));
18+
}
19+
20+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package io.quarkus.gradle;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.io.ObjectInputStream;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.StandardCopyOption;
11+
12+
import org.junit.jupiter.api.Test;
13+
14+
import io.quarkus.bootstrap.model.ApplicationModel;
15+
import io.quarkus.maven.dependency.DependencyFlags;
16+
17+
public class TestCompositeBuildWithExtensionsTest extends QuarkusGradleWrapperTestBase {
18+
19+
@Test
20+
public void compositeBuildWithExtensions() throws Exception {
21+
final File projectDir = getProjectDir("multi-composite-build-extensions-project");
22+
23+
final BuildResult result = runGradleWrapper(projectDir, ":application:clean", ":application:test");
24+
25+
assertThat(BuildResult.isSuccessful(result.getTasks().get(":application:test"))).isTrue();
26+
27+
final Path testAppModelDat = projectDir.toPath().resolve("application").resolve("build").resolve("quarkus")
28+
.resolve("application-model").resolve("quarkus-app-test-model.dat");
29+
assertThat(testAppModelDat).exists();
30+
31+
final ApplicationModel testModel = deserializeAppModel(testAppModelDat);
32+
for (var d : testModel.getDependencies(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT)) {
33+
assertFlagSet(d.getFlags(), DependencyFlags.RUNTIME_CP);
34+
assertFlagSet(d.getFlags(), DependencyFlags.DEPLOYMENT_CP);
35+
assertFlagSet(d.getFlags(), DependencyFlags.RUNTIME_EXTENSION_ARTIFACT);
36+
assertFlagSet(d.getFlags(), DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT);
37+
assertFlagNotSet(d.getFlags(), DependencyFlags.RELOADABLE);
38+
}
39+
}
40+
41+
private static void assertFlagSet(int actualFlags, int expectedFlag) {
42+
assertThat(actualFlags & expectedFlag).isEqualTo(expectedFlag);
43+
}
44+
45+
private static void assertFlagNotSet(int actualFlags, int expectedFlag) {
46+
assertThat(actualFlags & expectedFlag).isZero();
47+
}
48+
49+
private File testProjectDir;
50+
51+
@Override
52+
protected File getProjectDir(String projectName) {
53+
if (testProjectDir == null) {
54+
File projectDir = super.getProjectDir(projectName);
55+
final File appProperties = new File(projectDir, "application/gradle.properties");
56+
final File libsProperties = new File(projectDir, "libraries/gradle.properties");
57+
final File extensionProperties = new File(projectDir, "extensions/example-extension/gradle.properties");
58+
final File anotherExtensionProperties = new File(projectDir,
59+
"extensions/another-example-extension/gradle.properties");
60+
final Path projectProperties = projectDir.toPath().resolve("gradle.properties");
61+
62+
try {
63+
Files.copy(projectProperties, appProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
64+
Files.copy(projectProperties, libsProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
65+
Files.copy(projectProperties, extensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
66+
Files.copy(projectProperties, anotherExtensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
67+
} catch (IOException e) {
68+
throw new IllegalStateException("Unable to copy gradle.properties file", e);
69+
}
70+
this.testProjectDir = projectDir;
71+
}
72+
return testProjectDir;
73+
}
74+
75+
private static ApplicationModel deserializeAppModel(Path path) throws IOException {
76+
try (ObjectInputStream out = new ObjectInputStream(Files.newInputStream(path))) {
77+
return (ApplicationModel) out.readObject();
78+
} catch (ClassNotFoundException e) {
79+
throw new RuntimeException(e);
80+
}
81+
}
82+
}

integration-tests/gradle/src/test/java/io/quarkus/gradle/devmode/MultiCompositeBuildExtensionsDevModeTest.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,29 @@ protected void testDevMode() throws Exception {
4141
assertThat(getHttpResponse("/hello")).contains("hello from LibB and LibA extension enabled: true");
4242
}
4343

44+
private File testProjectDir;
45+
4446
@Override
4547
protected File getProjectDir() {
46-
File projectDir = super.getProjectDir();
47-
final File appProperties = new File(projectDir, "application/gradle.properties");
48-
final File libsProperties = new File(projectDir, "libraries/gradle.properties");
49-
final File extensionProperties = new File(projectDir, "extensions/example-extension/gradle.properties");
50-
final File anotherExtensionProperties = new File(projectDir, "extensions/another-example-extension/gradle.properties");
51-
final Path projectProperties = projectDir.toPath().resolve("gradle.properties");
52-
53-
try {
54-
Files.copy(projectProperties, appProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
55-
Files.copy(projectProperties, libsProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
56-
Files.copy(projectProperties, extensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
57-
Files.copy(projectProperties, anotherExtensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
58-
} catch (IOException e) {
59-
throw new IllegalStateException("Unable to copy gradle.properties file", e);
48+
if (testProjectDir == null) {
49+
File projectDir = super.getProjectDir();
50+
final File appProperties = new File(projectDir, "application/gradle.properties");
51+
final File libsProperties = new File(projectDir, "libraries/gradle.properties");
52+
final File extensionProperties = new File(projectDir, "extensions/example-extension/gradle.properties");
53+
final File anotherExtensionProperties = new File(projectDir,
54+
"extensions/another-example-extension/gradle.properties");
55+
final Path projectProperties = projectDir.toPath().resolve("gradle.properties");
56+
57+
try {
58+
Files.copy(projectProperties, appProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
59+
Files.copy(projectProperties, libsProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
60+
Files.copy(projectProperties, extensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
61+
Files.copy(projectProperties, anotherExtensionProperties.toPath(), StandardCopyOption.REPLACE_EXISTING);
62+
} catch (IOException e) {
63+
throw new IllegalStateException("Unable to copy gradle.properties file", e);
64+
}
65+
this.testProjectDir = projectDir;
6066
}
61-
return projectDir;
67+
return testProjectDir;
6268
}
6369
}

0 commit comments

Comments
 (0)