Skip to content

Commit

Permalink
Parse JSON with openjson
Browse files Browse the repository at this point in the history
Code originally contributed by @snicoll.

This commit upgrades the use of Jackson to plain old JSONObject. This
has the advantage of significantly reduce the size of required
dependencies, in particular for the graalvm-reachability-metadata
module that is meant to be reused externally.
  • Loading branch information
sdeleuze committed Dec 23, 2024
1 parent e05d314 commit 2628d39
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 554 deletions.
494 changes: 23 additions & 471 deletions THIRD_PARTY_LICENSES.txt

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion common/graalvm-reachability-metadata/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ maven {
}

dependencies {
implementation(libs.jackson.databind)
implementation(libs.openjson)
testImplementation(platform(libs.test.junit.bom))
testImplementation(libs.test.junit.jupiter.core)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,9 @@
*/
package org.graalvm.reachability.internal.index.artifacts;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Set;
import java.util.regex.Pattern;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Artifact {
private final String module;
private final Set<String> versions;
Expand All @@ -56,13 +51,8 @@ public class Artifact {
private final boolean override;
private final Pattern defaultForPattern;

@JsonCreator
public Artifact(@JsonProperty("module") String module,
@JsonProperty("tested-versions") Set<String> versions,
@JsonProperty("metadata-version") String directory,
@JsonProperty(value = "latest", defaultValue = "false") boolean latest,
@JsonProperty(value = "override", defaultValue = "false") boolean override,
@JsonProperty(value = "default-for") String defaultFor) {
public Artifact(String module, Set<String> versions, String directory,
boolean latest, boolean override, String defaultFor) {
this.module = module;
this.versions = versions;
this.directory = directory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,20 @@
*/
package org.graalvm.reachability.internal.index.artifacts;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;
import org.graalvm.reachability.DirectoryConfiguration;
import org.graalvm.reachability.internal.UncheckedIOException;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

Expand All @@ -66,13 +68,13 @@ public SingleModuleJsonVersionToConfigDirectoryIndex(Path moduleRoot) {

private Map<String, List<Artifact>> parseIndexFile(Path rootPath) {
Path indexFile = rootPath.resolve("index.json");
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
try (BufferedReader reader = Files.newBufferedReader(indexFile)) {
List<Artifact> entries = objectMapper.readValue(
reader,
typeFactory.constructCollectionType(List.class, Artifact.class)
);
try {
String fileContent = Files.readString(indexFile);
JSONArray json = new JSONArray(fileContent);
List<Artifact> entries = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
entries.add(fromJson(json.getJSONObject(i)));
}
return entries.stream()
.collect(Collectors.groupingBy(Artifact::getModule));
} catch (IOException e) {
Expand Down Expand Up @@ -132,4 +134,24 @@ private Optional<DirectoryConfiguration> findConfigurationFor(String groupId, St
moduleRoot.resolve(artifact.getDirectory()), artifact.isOverride()));
}

private Artifact fromJson(JSONObject json) {
String module = json.optString("module", null);
Set<String> testVersions = readTestedVersions(json.optJSONArray("tested-versions"));
String directory = json.optString("metadata-version", null);
boolean latest = json.optBoolean("latest");
boolean override = json.optBoolean("override");
String defaultFor = json.optString("default-for", null);
return new Artifact(module, testVersions, directory, latest, override, defaultFor);
}

private Set<String> readTestedVersions(JSONArray array) {
Set<String> testVersions = new LinkedHashSet<>();
if (array != null) {
for (int i = 0; i < array.length(); i++) {
testVersions.add(array.getString(i));
}
}
return testVersions;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@
*/
package org.graalvm.reachability.internal.index.modules;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;
import org.graalvm.reachability.internal.UncheckedIOException;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand All @@ -68,13 +68,13 @@ public JsonModuleToConfigDirectoryIndex(Path rootPath) {

private Map<String, Set<Path>> parseIndexFile(Path rootPath) {
Path indexFile = rootPath.resolve("index.json");
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
try (BufferedReader reader = Files.newBufferedReader(indexFile)) {
List<ModuleEntry> entries = objectMapper.readValue(
reader,
typeFactory.constructCollectionType(List.class, ModuleEntry.class)
);
try {
String fileContent = Files.readString(indexFile);
JSONArray json = new JSONArray(fileContent);
List<ModuleEntry> entries = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
entries.add(fromJson(json.getJSONObject(i)));
}
Map<String, List<ModuleEntry>> moduleToEntries = entries.stream()
.collect(Collectors.groupingBy(ModuleEntry::getModule));
Map<String, Set<Path>> index = new HashMap<>(moduleToEntries.size());
Expand Down Expand Up @@ -104,6 +104,23 @@ private Map<String, Set<Path>> parseIndexFile(Path rootPath) {

}

private ModuleEntry fromJson(JSONObject json) {
String module = json.optString("module", null);
String moduleDirectory = json.optString("directory", null);
List<String> requires = readRequires(json.optJSONArray("requires"));
return new ModuleEntry(module, moduleDirectory, requires);
}

private List<String> readRequires(JSONArray array) {
List<String> requires = new ArrayList<>();
if (array != null) {
for (int i = 0; i < array.length(); i++) {
requires.add(array.getString(i));
}
}
return requires;
}

/**
* Returns the directory containing the candidate configurations for the given module.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,15 @@
*/
package org.graalvm.reachability.internal.index.modules;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Collections;
import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public class ModuleEntry {
private final String module;
private final String moduleDirectory;
private final List<String> requires;

@JsonCreator
public ModuleEntry(@JsonProperty("module") String module,
@JsonProperty("directory") String moduleDirectory,
@JsonProperty("requires") List<String> requires) {
public ModuleEntry(String module, String moduleDirectory, List<String> requires) {
this.module = module;
this.moduleDirectory = moduleDirectory;
this.requires = requires == null ? Collections.emptyList() : requires;
Expand Down
2 changes: 1 addition & 1 deletion common/utils/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ maven {
}

dependencies {
implementation(libs.jackson.databind)
implementation(libs.openjson)
testImplementation(platform(libs.test.junit.bom))
testImplementation(libs.test.junit.jupiter.core)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
*/
package org.graalvm.buildtools.model.resources;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
Expand All @@ -50,13 +51,40 @@

public class ResourcesConfigModelSerializer {
public static void serialize(ResourcesConfigModel model, File outputFile) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model);
JSONObject json = toJson(model);
String pretty = json.toString(4);
File outputDir = outputFile.getParentFile();
if (outputDir.isDirectory() || outputDir.mkdirs()) {
try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
out.append(pretty);
}
}
}

private static JSONObject toJson(ResourcesConfigModel model) {
JSONObject json = new JSONObject();
json.put("resources", toJson(model.getResources()));
JSONArray namedValues = new JSONArray();
model.getBundles().forEach(namedValue -> namedValues.put(toJson(namedValue)));
json.put("bundles", namedValues);
return json;
}

private static JSONObject toJson(ResourcesModel model) {
JSONObject json = new JSONObject();
JSONArray includes = new JSONArray();
model.getIncludes().forEach(includes::put);
json.put("includes", includes);
JSONArray excludes = new JSONArray();
model.getExcludes().forEach(excludes::put);
json.put("excludes", excludes);
return json;
}

private static JSONObject toJson(NamedValue namedValue) {
JSONObject json = new JSONObject();
json.put("name", namedValue.getName());
return json;
}

}
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mavenAnnotations = "3.6.4"
mavenEmbedder = "3.8.6"
mavenWagon = "3.4.3"
graalvm = "23.0.2"
jackson = "2.13.5"
openjson = "1.0.13"
junitPlatform = "1.10.0"
junitJupiter = "5.10.0"
aether = "1.1.0"
Expand Down Expand Up @@ -42,7 +42,7 @@ test-spock = { module = "org.spockframework:spock-core", version.ref = "spock" }

graalvm-svm = { module = "org.graalvm.nativeimage:svm", version.ref = "graalvm" }

jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
openjson = { module = "com.github.openjson:openjson", version.ref = "openjson" }

maven-pluginApi = { module = "org.apache.maven:maven-plugin-api", version.ref = "maven" }
maven-pluginAnnotations = { module = "org.apache.maven.plugin-tools:maven-plugin-annotations", version.ref = "mavenAnnotations" }
Expand Down
2 changes: 1 addition & 1 deletion native-maven-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ maven {

dependencies {
implementation(libs.utils)
implementation(libs.jackson.databind)
implementation(libs.openjson)
implementation(libs.jvmReachabilityMetadata)
implementation(libs.plexus.utils)
implementation(libs.plexus.xml)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@

package org.graalvm.buildtools.maven

import com.fasterxml.jackson.databind.node.ObjectNode
import com.github.openjson.JSONObject
import org.graalvm.buildtools.maven.sbom.SBOMGenerator
import org.graalvm.buildtools.utils.NativeImageUtils
import spock.lang.Requires
import com.fasterxml.jackson.databind.ObjectMapper

class SBOMFunctionalTest extends AbstractGraalVMMavenFunctionalTest {
private static boolean EE() {
Expand Down Expand Up @@ -143,12 +142,11 @@ class SBOMFunctionalTest extends AbstractGraalVMMavenFunctionalTest {
return false
}
def mapper = new ObjectMapper()
def rootNode = mapper.readTree(sbom)
def rootNode = new JSONObject(sbom.getText())
// Check root fields
assert rootNode.has('bomFormat')
assert rootNode.get('bomFormat').asText() == 'CycloneDX'
assert rootNode.getString('bomFormat') == 'CycloneDX'
assert rootNode.has('specVersion')
assert rootNode.has('serialNumber')
assert rootNode.has('version')
Expand All @@ -157,20 +155,20 @@ class SBOMFunctionalTest extends AbstractGraalVMMavenFunctionalTest {
assert rootNode.has('dependencies')
// Check metadata/component
def metadataComponent = rootNode.path('metadata').path('component')
def metadataComponent = rootNode.getJSONObject('metadata').getJSONObject('component')
assert metadataComponent.has('group')
assert metadataComponent.get('group').asText() == 'org.graalvm.buildtools.examples'
assert metadataComponent.getString('group') == 'org.graalvm.buildtools.examples'
assert metadataComponent.has('name')
assert metadataComponent.get('name').asText() == 'maven'
assert metadataComponent.getString('name') == 'maven'
// Check that components and dependencies are non-empty
assert !rootNode.get('components').isEmpty()
assert !rootNode.get('dependencies').isEmpty()
assert !rootNode.getJSONArray('components').isEmpty()
assert !rootNode.getJSONArray('dependencies').isEmpty()
// Check that the main component has no dependencies
def mainComponentId = metadataComponent.get('bom-ref').asText()
def mainComponentDependency = rootNode.get('dependencies').find { it.get('ref').asText() == mainComponentId } as ObjectNode
assert mainComponentDependency.get('dependsOn').isEmpty()
def mainComponentId = metadataComponent.getString('bom-ref')
def mainComponentDependency = rootNode.getJSONArray('dependencies').iterator().find { it.getString('ref') == mainComponentId } as JSONObject
assert mainComponentDependency.getJSONArray('dependsOn').isEmpty()
// Check that the main component is not found in "components"
assert !rootNode.get('components').any { it.get('bom-ref').asText() == mainComponentId }
Expand Down
Loading

0 comments on commit 2628d39

Please sign in to comment.