Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the possibility to override values while generating the extension.json as described by issue #4320 #4861

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,20 +1,25 @@
package io.quarkus.maven;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.json.JsonWriter;
import javax.json.JsonWriterFactory;
import javax.json.stream.JsonGenerator;
Expand Down Expand Up @@ -59,6 +64,9 @@ public class GenerateExtensionsJsonMojo extends AbstractMojo {
@Parameter(property = "bomVersion", defaultValue = "${project.version}")
private String bomVersion;

@Parameter(property = "overridesFile", defaultValue = "${project.basedir}/src/main/resources/extensions-overrides.json")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems fishy - users can ovveride the name of resources folder - isn't there a more maven agnostic way to look these up ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

${project.build.outputDirectory} the resources will end up being there, also possibly filtered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ${project.build.outputDirectory} doesn't seem to work. There's no output directory basically (there's just target, no target/classes)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I was using ${project.resources[0].resource} but that doesn't work. The expression is probably too complex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why is this fishy? All paths are basically overridable, right? I mean is there some way this could be abused?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this the defaultvalue...oh well then i don't care at this stage - maybe post 1.0 if ever ;)

private File overridesFile;

@Parameter(property = "outputFile", defaultValue = "${project.build.directory}/extensions.json")
private File outputFile;

Expand All @@ -83,9 +91,11 @@ public GenerateExtensionsJsonMojo() {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {

// Get the BOM artifact
final DefaultArtifact bomArtifact = new DefaultArtifact(bomGroupId, bomArtifactId, "", "pom", bomVersion);
debug("Generating catalog of extensions for %s", bomArtifact);
info("Generating catalog of extensions for %s", bomArtifact);

// And get all its dependencies (which are extensions)
final List<Dependency> deps;
try {
deps = repoSystem
Expand All @@ -100,6 +110,27 @@ public void execute() throws MojoExecutionException, MojoFailureException {
return;
}

// Read the overrides file for the extensions (if it exists)
Map<String, JsonObject> extOverrides = new HashMap<>();
if (overridesFile.isFile()) {
info("Found overrides file %s", overridesFile);
try (JsonReader jsonReader = Json.createReader(new FileInputStream(overridesFile))) {
JsonObject overridesObject = jsonReader.readObject();
JsonArray extOverrideObjects = overridesObject.getJsonArray("extensions");
if (extOverrideObjects != null) {
// Put the extension overrides into a map keyed to their GAV
for (JsonValue val : extOverrideObjects) {
JsonObject extOverrideObject = val.asJsonObject();
String key = extensionId(extOverrideObject);
extOverrides.put(key, extOverrideObject);
}
}
} catch (IOException e) {
throw new MojoExecutionException("Failed to read " + overridesFile, e);
}
}

// Create a JSON array of extension descriptors
final JsonArrayBuilder extListJson = Json.createArrayBuilder();
for (Dependency dep : deps) {
final Artifact artifact = dep.getArtifact();
Expand All @@ -113,7 +144,15 @@ public void execute() throws MojoExecutionException, MojoFailureException {
try {
resolved = repoSystem.resolveArtifact(repoSession,
new ArtifactRequest().setRepositories(repos).setArtifact(artifact));
processDependency(resolved.getArtifact(), extListJson);
JsonObject extObject = processDependency(resolved.getArtifact());
if (extObject != null) {
String key = extensionId(extObject);
JsonObject extOverride = extOverrides.get(key);
if (extOverride != null) {
extObject = mergeObject(extObject, extOverride);
}
extListJson.add(extObject);
}
} catch (ArtifactResolutionException e) {
// there are some parent poms that appear as jars for some reason
debug("Failed to resolve dependency %s defined in %s", artifact, bomArtifact);
Expand All @@ -122,14 +161,18 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
}

// Create the toplevel JSON
final JsonObjectBuilder platformJson = Json.createObjectBuilder();
// Add information about the BOM to it
final JsonObjectBuilder bomJson = Json.createObjectBuilder();
bomJson.add("groupId", bomGroupId);
bomJson.add("artifactId", bomArtifactId);
bomJson.add("version", bomVersion);
platformJson.add("bom", bomJson.build());
// And add the list of extensions
platformJson.add("extensions", extListJson.build());

// Write the JSON to the output file
final File outputDir = outputFile.getParentFile();
if (!outputDir.exists()) {
if (!outputDir.mkdirs()) {
Expand All @@ -143,56 +186,99 @@ public void execute() throws MojoExecutionException, MojoFailureException {
} catch (IOException e) {
throw new MojoExecutionException("Failed to persist " + outputFile, e);
}
info("Extensions file written to %s", outputFile);

projectHelper.attachArtifact(project, "json", null, outputFile);
}

private void processDependency(Artifact artifact, JsonArrayBuilder extJsonBuilder) throws IOException {
private JsonObject processDependency(Artifact artifact) throws IOException {
final Path path = artifact.getFile().toPath();
if (Files.isDirectory(path)) {
processMetaInfDir(artifact, path.resolve(BootstrapConstants.META_INF), extJsonBuilder);
return processMetaInfDir(artifact, path.resolve(BootstrapConstants.META_INF));
} else {
try (FileSystem artifactFs = ZipUtils.newFileSystem(path)) {
processMetaInfDir(artifact, artifactFs.getPath(BootstrapConstants.META_INF), extJsonBuilder);
return processMetaInfDir(artifact, artifactFs.getPath(BootstrapConstants.META_INF));
}
}
}

private boolean processMetaInfDir(Artifact artifact, Path metaInfDir, JsonArrayBuilder extJsonBuilder)
private JsonObject processMetaInfDir(Artifact artifact, Path metaInfDir)
throws IOException {
if (!Files.exists(metaInfDir)) {
return false;
return null;
}
final Path p = metaInfDir.resolve(BootstrapConstants.EXTENSION_PROPS_JSON_FILE_NAME);
if (!Files.exists(p)) {
final Path props = metaInfDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
if (Files.exists(props)) {
extJsonBuilder.add(Json.createObjectBuilder()
return Json.createObjectBuilder()
.add("artifactId", artifact.getArtifactId())
.add("groupId", artifact.getGroupId())
.add("version", artifact.getVersion())
.add("name", artifact.getArtifactId())
.build());
.build();
}
return false;
return null;
}
processPlatformArtifact(artifact, p, extJsonBuilder);
return true;
return processPlatformArtifact(artifact, p);
}

private void processPlatformArtifact(Artifact artifact, Path descriptor, JsonArrayBuilder extJsonBuilder)
private JsonObject processPlatformArtifact(Artifact artifact, Path descriptor)
throws IOException {
try (InputStream is = Files.newInputStream(descriptor)) {
try (JsonReader reader = Json.createReader(is)) {
final JsonObject object = reader.readObject();
debug("Adding Quarkus extension %s:%s", object.get("groupId"), object.get("artifactId"));
extJsonBuilder.add(object);
return object;
}
} catch (IOException e) {
throw new IOException("Failed to parse " + descriptor, e);
}
}

private String extensionId(JsonObject extObject) {
String artId = extObject.getString("artifactId", "");
if (artId.isEmpty()) {
getLog().warn("Missing artifactId in extension overrides");
}
String groupId = extObject.getString("artifactId", "");
if (groupId.isEmpty()) {
return artId;
} else {
return extObject.getString("groupId", "") + ":" + artId;
}
}

private JsonObject mergeObject(JsonObject extObject, JsonObject extOverride) {
final JsonObjectBuilder mergedObject = Json.createObjectBuilder();
for (Map.Entry<String, JsonValue> e : extOverride.entrySet()) {
JsonValue.ValueType tp = e.getValue().getValueType();
if (tp == JsonValue.ValueType.OBJECT
&& extObject.containsKey(e.getKey())
&& extObject.get(e.getKey()).getValueType() == JsonValue.ValueType.OBJECT) {
mergedObject.add(e.getKey(),
mergeObject(extObject.get(e.getKey()).asJsonObject(), e.getValue().asJsonObject()));
} else if (tp == JsonValue.ValueType.ARRAY) {
final JsonArrayBuilder newArray = Json.createArrayBuilder(e.getValue().asJsonArray());
mergedObject.add(e.getKey(), newArray.build());
} else {
mergedObject.add(e.getKey(), e.getValue());
}
}
return mergedObject.build();
}

private void info(String msg, Object... args) {
if (!getLog().isInfoEnabled()) {
return;
}
if (args.length == 0) {
getLog().info(msg);
return;
}
getLog().info(String.format(msg, args));
}

private void debug(String msg, Object... args) {
if (!getLog().isDebugEnabled()) {
return;
Expand Down