Skip to content

Commit

Permalink
Feature: JAR Files: If there are multiple artifacts with the same che…
Browse files Browse the repository at this point in the history
…cksum, include them all in column "Artifact coordinates".
  • Loading branch information
smarkwal committed Sep 24, 2024
1 parent 85726a8 commit 9e78301
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ JAR Files
List of JAR files found in classpath.

JAR file | Size | Classes | Resources | Checksum (SHA-1) | Artifact coordinates
---------------------+---------+---------+-----------+------------------------------------------+---------------------------
---------------------+---------+---------+-----------+------------------------------------------+-------------------------------------------
slf4j-api-1.7.28.jar | 40.2 KB | 34 | 0 | 2cd9b264f76e3d087ee21bfc99305928e1bdb443 | org.slf4j:slf4j-api:1.7.28
| | | | | org.netbeans.external:slf4j-api:RELEASE113
Classpath | 40.2 KB | 34 | 0 | - | -

Modules
Expand Down
53 changes: 30 additions & 23 deletions jarhc/src/main/java/org/jarhc/analyzer/DependenciesAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,13 @@ private String getStatus(List<Dependency> dependencies, Classpath classpath) {

// check if it is an exact match
StringBuilder line = new StringBuilder("OK");
String coordinates = jarFile.getCoordinates();
Artifact artifact = new Artifact(coordinates);
if (!artifact.equals(dependency.toArtifact())) {
line.append(" (version ").append(artifact.getVersion()).append(")");
Artifact artifact = jarFile.getArtifacts().get(0);
if (artifact.getGroupId().equals(dependency.getGroupId()) && artifact.getArtifactId().equals(dependency.getArtifactId())) {
if (!artifact.getVersion().equals(dependency.getVersion())) {
line.append(" (version ").append(artifact.getVersion()).append(")");
}
} else {
line.append(" (").append(artifact.toCoordinates()).append(")");
}

String classLoader = jarFile.getClassLoader();
Expand All @@ -160,38 +163,42 @@ private String getStatus(List<Dependency> dependencies, Classpath classpath) {

private boolean matches(JarFile jarFile, Dependency dependency) {

String coordinates = jarFile.getCoordinates();
if (coordinates == null) {
List<Artifact> artifacts = jarFile.getArtifacts();
if (artifacts == null || artifacts.isEmpty()) {
// skip JAR files without coordinates
return false;
}

Artifact jarArtifact = new Artifact(coordinates);
Artifact dependencyArtifact = dependency.toArtifact();
for (Artifact artifact : artifacts) {

// check if group ID matches
if (!Objects.equals(jarArtifact.getGroupId(), dependencyArtifact.getGroupId())) {
return false;
}
// check if group ID matches
if (!Objects.equals(artifact.getGroupId(), dependency.getGroupId())) {
continue;
}

// check if artifact ID matches
if (!Objects.equals(jarArtifact.getArtifactId(), dependencyArtifact.getArtifactId())) {
return false;
}
// check if artifact ID matches
if (!Objects.equals(artifact.getArtifactId(), dependency.getArtifactId())) {
continue;
}

// TODO: check if type matches?
// TODO: check if type matches?

// note: version may be different
return true;
}

// note: version may be different
return true;
return false;
}

private String getCoordinates(JarFile jarFile) {
// TODO: return "[timeout]" if Maven Search API request timed out
String coordinates = jarFile.getCoordinates();
if (coordinates == null) {
List<Artifact> artifacts = jarFile.getArtifacts();
if (artifacts == null) {
return ERROR; // most likely a response timeout
} else if (artifacts.isEmpty()) {
return UNKNOWN;
}
return coordinates;
// return only coordinates of "primary" artifact
return artifacts.get(0).toCoordinates();
}

}
13 changes: 9 additions & 4 deletions jarhc/src/main/java/org/jarhc/analyzer/JarFilesAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
import static org.jarhc.utils.FileUtils.formatFileSize;

import java.util.List;
import org.jarhc.artifacts.Artifact;
import org.jarhc.model.ClassDef;
import org.jarhc.model.Classpath;
import org.jarhc.model.JarFile;
import org.jarhc.report.ReportSection;
import org.jarhc.report.ReportTable;
import org.jarhc.utils.StringUtils;

public class JarFilesAnalyzer implements Analyzer {

private static final String UNKNOWN = "[unknown]";
private static final String ERROR = "[error]";

@Override
public ReportSection analyze(Classpath classpath) {
Expand Down Expand Up @@ -80,12 +83,14 @@ private String getChecksumInfo(JarFile jarFile) {
}

private String getCoordinates(JarFile jarFile) {
// TODO: return "[timeout]" if Maven Search API request timed out
String coordinates = jarFile.getCoordinates();
if (coordinates == null || coordinates.isEmpty()) {
List<Artifact> artifacts = jarFile.getArtifacts();
if (artifacts == null) {
return ERROR; // most likely a response timeout
} else if (artifacts.isEmpty()) {
return UNKNOWN;
}
return coordinates;
// return coordinates of all artifacts
return artifacts.stream().map(Artifact::toCoordinates).collect(StringUtils.joinLines());
}

}
15 changes: 6 additions & 9 deletions jarhc/src/main/java/org/jarhc/loader/AbstractFileLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,19 +215,16 @@ protected void load(String fileName, Archive archive, List<JarFile> jarFiles) th
String checksum = archive.getFileChecksum();

// try to identify JAR file as Maven artifact
// TODO: if artifact was given as coordinates, skip this step and re-use the original coordinates instead.
String coordinates = null;
List<Artifact> artifacts = null;
try {
List<Artifact> artifacts = repository.findArtifacts(checksum);
// TODO: remember all artifact coordinates for this JAR file
if (!artifacts.isEmpty()) {
Artifact artifact = artifacts.get(0);
coordinates = artifact.toCoordinates();
}
artifacts = repository.findArtifacts(checksum);
} catch (RepositoryException e) {
logger.warn("Artifact resolution error", e);
}

// TODO: if artifact was given as coordinates, make sure these coordinates are included in the list of artifacts
// TODO: check if JAR file contains POM information

// normalize file name (optional)
if (fileNameNormalizer != null) {
fileName = fileNameNormalizer.getFileName(fileName, checksum);
Expand All @@ -236,7 +233,7 @@ protected void load(String fileName, Archive archive, List<JarFile> jarFiles) th
JarFile jarFile = JarFile.withName(fileName)
.withFileSize(archive.getFileSize())
.withChecksum(checksum)
.withCoordinates(coordinates)
.withArtifacts(artifacts)
.withClassLoader(classLoader)
.withReleases(releases)
.withModuleInfo(moduleInfo)
Expand Down
21 changes: 11 additions & 10 deletions jarhc/src/main/java/org/jarhc/model/JarFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.jarhc.artifacts.Artifact;

/**
* Represents the content of a JAR file.
Expand All @@ -49,7 +50,7 @@ public class JarFile {
/**
* Maven coordinates of the JAR file
*/
private final String coordinates;
private final List<Artifact> artifacts;

/**
* Name of the class loader used to load this JAR file.
Expand Down Expand Up @@ -97,7 +98,7 @@ public class JarFile {
* @param fileName JAR file name
* @param fileSize JAR file size in bytes
* @param checksum JAR file SHA-1 checksum
* @param coordinates JAR file Maven coordinates
* @param artifacts List of Maven coordinates
* @param classLoader Class loader name
* @param releases List of releases supported by this JAR file (for multi-release JAR files)
* @param moduleInfo Module information (for modular JAR files)
Expand All @@ -106,15 +107,15 @@ public class JarFile {
* @throws IllegalArgumentException If <code>fileName</code> or <code>classDefs</code> is <code>null</code>.
*/
@SuppressWarnings("java:S107") // Methods should not have too many parameters
private JarFile(String fileName, long fileSize, String checksum, String coordinates, String classLoader, Set<Integer> releases, ModuleInfo moduleInfo, List<ClassDef> classDefs, List<ResourceDef> resourceDefs) {
private JarFile(String fileName, long fileSize, String checksum, List<Artifact> artifacts, String classLoader, Set<Integer> releases, ModuleInfo moduleInfo, List<ClassDef> classDefs, List<ResourceDef> resourceDefs) {
if (fileName == null) throw new IllegalArgumentException("fileName");
if (releases == null) throw new IllegalArgumentException("releases");
if (moduleInfo == null) throw new IllegalArgumentException("moduleInfo");
if (classDefs == null) throw new IllegalArgumentException("classDefs");
this.fileName = fileName;
this.fileSize = fileSize;
this.checksum = checksum;
this.coordinates = coordinates;
this.artifacts = artifacts;
this.classLoader = classLoader;
this.releases = new TreeSet<>(releases);
this.moduleInfo = moduleInfo;
Expand Down Expand Up @@ -171,8 +172,8 @@ public String getChecksum() {
return checksum;
}

public String getCoordinates() {
return coordinates;
public List<Artifact> getArtifacts() {
return artifacts;
}

public String getClassLoader() {
Expand Down Expand Up @@ -256,7 +257,7 @@ public static class Builder {
private final String fileName;
private long fileSize = -1;
private String checksum;
private String coordinates;
private List<Artifact> artifacts;
private String classLoader;
private final Set<Integer> releases = new TreeSet<>();
private ModuleInfo moduleInfo = ModuleInfo.UNNAMED;
Expand All @@ -277,8 +278,8 @@ public Builder withChecksum(String checksum) {
return this;
}

public Builder withCoordinates(String coordinates) {
this.coordinates = coordinates;
public Builder withArtifacts(List<Artifact> artifacts) {
this.artifacts = artifacts;
return this;
}

Expand Down Expand Up @@ -319,7 +320,7 @@ public Builder withResourceDefs(Collection<ResourceDef> resourceDefs) {
}

public JarFile build() {
return new JarFile(fileName, fileSize, checksum, coordinates, classLoader, releases, moduleInfo, classDefs, resourceDefs);
return new JarFile(fileName, fileSize, checksum, artifacts, classLoader, releases, moduleInfo, classDefs, resourceDefs);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,19 @@ void analyze() {

// prepare: provided
List<JarFile> jarFiles = new ArrayList<>();
jarFiles.add(JarFile.withName("lib-with-deps-1.jar").withCoordinates("group:lib-with-deps-1:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-with-deps-2.jar").withCoordinates("group:lib-with-deps-2:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-with-deps-3.jar").withCoordinates("group:lib-with-deps-3:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-with-deps-4.jar").withCoordinates("group:lib-with-deps-4:1.1:jar").build());
jarFiles.add(JarFile.withName("lib-with-deps-1.jar").withArtifacts(List.of(new Artifact("group:lib-with-deps-1:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-with-deps-2.jar").withArtifacts(List.of(new Artifact("group:lib-with-deps-2:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-with-deps-3.jar").withArtifacts(List.of(new Artifact("group:lib-with-deps-3:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-with-deps-4.jar").withArtifacts(List.of(new Artifact("group:lib-with-deps-4:1.1:jar"))).build());
Classpath provided = new Classpath(jarFiles, null, ClassLoaderStrategy.ParentLast);

// prepare: classpath
jarFiles = new ArrayList<>();
jarFiles.add(JarFile.withName("lib-with-deps.jar").withCoordinates("group:lib-with-deps:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-no-deps.jar").withCoordinates("group:lib-no-deps:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-no-pom.jar").withCoordinates("group:lib-no-pom:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-repo-error.jar").withCoordinates("group:lib-repo-error:1.0:jar").build());
jarFiles.add(JarFile.withName("lib-unknown.jar").withCoordinates(null).build());
jarFiles.add(JarFile.withName("lib-with-deps.jar").withArtifacts(List.of(new Artifact("group:lib-with-deps:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-no-deps.jar").withArtifacts(List.of(new Artifact("group:lib-no-deps:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-no-pom.jar").withArtifacts(List.of(new Artifact("group:lib-no-pom:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-repo-error.jar").withArtifacts(List.of(new Artifact("group:lib-repo-error:1.0:jar"))).build());
jarFiles.add(JarFile.withName("lib-unknown.jar").withArtifacts(List.of()).build());
Classpath classpath = new Classpath(jarFiles, provided, ClassLoaderStrategy.ParentLast);

Logger logger = LoggerBuilder.collect(DependenciesAnalyzer.class);
Expand All @@ -103,15 +103,15 @@ void analyze() {
List<String[]> rows = table.getRows();
assertEquals(5, rows.size());

assertValuesEquals(rows.get(0), "lib-with-deps.jar", "group:lib-with-deps:1.0:jar", "group:lib-with-deps-1:1.0 (provided, optional)\ngroup:lib-with-deps-2:1.0 (runtime)\ngroup:lib-with-deps-4:1.0 (system)\ngroup:lib-with-deps-5:1.0 (import, optional)", "OK\nOK\nOK (version 1.1)\nUnsatisfied");
assertValuesEquals(rows.get(1), "lib-no-deps.jar", "group:lib-no-deps:1.0:jar", "[none]", "");
assertValuesEquals(rows.get(2), "lib-no-pom.jar", "group:lib-no-pom:1.0:jar", "[error]", "");
assertValuesEquals(rows.get(3), "lib-repo-error.jar", "group:lib-repo-error:1.0:jar", "[error]", "");
assertValuesEquals(rows.get(0), "lib-with-deps.jar", "group:lib-with-deps:1.0", "group:lib-with-deps-1:1.0 (provided, optional)\ngroup:lib-with-deps-2:1.0 (runtime)\ngroup:lib-with-deps-4:1.0 (system)\ngroup:lib-with-deps-5:1.0 (import, optional)", "OK\nOK\nOK (version 1.1)\nUnsatisfied");
assertValuesEquals(rows.get(1), "lib-no-deps.jar", "group:lib-no-deps:1.0", "[none]", "");
assertValuesEquals(rows.get(2), "lib-no-pom.jar", "group:lib-no-pom:1.0", "[error]", "");
assertValuesEquals(rows.get(3), "lib-repo-error.jar", "group:lib-repo-error:1.0", "[error]", "");
assertValuesEquals(rows.get(4), "lib-unknown.jar", "[unknown]", "[unknown]", "");

assertLogger(logger).inAnyOrder()
.hasError("Resolver error for artifact: group:lib-no-pom:1.0:jar", new RepositoryException("test"))
.hasError("Resolver error for artifact: group:lib-repo-error:1.0:jar", new RepositoryException("test"))
.hasError("Resolver error for artifact: group:lib-no-pom:1.0", new RepositoryException("test"))
.hasError("Resolver error for artifact: group:lib-repo-error:1.0", new RepositoryException("test"))
.isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ void test_analyze() {

List<String[]> rows = table.getRows();
assertEquals(5, rows.size());
assertValuesEquals(rows.get(0), "a.jar", "128 B", "1", "1", "0a4c26b96ef92cceb7c2c7c0e19c808baeb8d696", "org.jarhc:0a4c2:1.0:jar");
assertValuesEquals(rows.get(1), "b.jar", "4.00 KB", "2", "0", "1271677b4f55e181e4c8192f0edf87bb3ff9fde5", "org.jarhc:12716:1.0:jar");
assertValuesEquals(rows.get(2), "c.jar", "23.4 KB", "1", "0", "fa2798370b42e2616cb0d374b2ae4be836439077", "org.jarhc:fa279:1.0:jar");
assertValuesEquals(rows.get(3), "d.jar", "1.18 MB", "0", "0", "458dea9210ea076f4c422be47390a9f2c0fcb0f8", "org.jarhc:458de:1.0:jar");
assertValuesEquals(rows.get(0), "a.jar", "128 B", "1", "1", "0a4c26b96ef92cceb7c2c7c0e19c808baeb8d696", "org.jarhc:0a4c2:1.0");
assertValuesEquals(rows.get(1), "b.jar", "4.00 KB", "2", "0", "1271677b4f55e181e4c8192f0edf87bb3ff9fde5", "org.jarhc:12716:1.0");
assertValuesEquals(rows.get(2), "c.jar", "23.4 KB", "1", "0", "fa2798370b42e2616cb0d374b2ae4be836439077", "org.jarhc:fa279:1.0");
assertValuesEquals(rows.get(3), "d.jar", "1.18 MB", "0", "0", "458dea9210ea076f4c422be47390a9f2c0fcb0f8", "org.jarhc:458de:1.0");
assertValuesEquals(rows.get(4), "Classpath", "1.20 MB", "4", "1", "-", "-");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.jarhc.artifacts.Artifact;
import org.jarhc.java.ClassLoader;
import org.jarhc.java.ClassLoaderStrategy;
import org.jarhc.model.ClassDef;
Expand Down Expand Up @@ -154,7 +155,7 @@ private void closeJarFile() {
JarFile jarFile = JarFile.withName(fileName)
.withFileSize(fileSize)
.withChecksum(checksum)
.withCoordinates(coordinates)
.withArtifacts(List.of(new Artifact(coordinates)))
.withReleases(releases)
.withModuleInfo(moduleInfo)
.withClassDefs(classDefs)
Expand Down

0 comments on commit 9e78301

Please sign in to comment.