Skip to content

Commit 4817626

Browse files
authored
Support Package URL (#286)
1 parent 6344b1d commit 4817626

File tree

5 files changed

+98
-0
lines changed

5 files changed

+98
-0
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,13 @@
312312
</exclusions>
313313
</dependency>
314314

315+
<!-- Package URL -->
316+
<dependency>
317+
<groupId>com.github.package-url</groupId>
318+
<artifactId>packageurl-java</artifactId>
319+
<version>1.5.0</version>
320+
</dependency>
321+
315322
<!-- Shared -->
316323
<dependency>
317324
<groupId>eu.maveniverse.maven.shared</groupId>

shared/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@
126126
<artifactId>jarviz-core</artifactId>
127127
</dependency>
128128

129+
<!-- Package URL -->
130+
<dependency>
131+
<groupId>com.github.package-url</groupId>
132+
<artifactId>packageurl-java</artifactId>
133+
</dependency>
134+
129135
<!-- Shared -->
130136
<dependency>
131137
<groupId>eu.maveniverse.maven.shared</groupId>

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/ToolboxCommando.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import static java.util.Objects.requireNonNull;
1111

12+
import com.github.packageurl.PackageURL;
1213
import eu.maveniverse.maven.mima.context.Context;
1314
import eu.maveniverse.maven.mima.context.ContextOverrides;
1415
import eu.maveniverse.maven.toolbox.shared.internal.ToolboxCommandoImpl;
@@ -183,6 +184,14 @@ default Collection<ResolutionRoot> loadGavs(Collection<String> gav, Collection<S
183184
*/
184185
Artifact toArtifact(Dependency dependency);
185186

187+
/**
188+
* Returns the package URL of the artifact in given remote repository, if possible.
189+
*
190+
* @see #remoteRepositoryUri(RemoteRepository)
191+
* @see <a href="https://github.com/package-url">Package URL</a>
192+
*/
193+
Optional<PackageURL> artifactPurl(RemoteRepository remoteRepository, Artifact artifact);
194+
186195
/**
187196
* Returns the URI of the artifact in given remote repository, if possible.
188197
*

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/ArtifactSinks.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static eu.maveniverse.maven.toolbox.shared.internal.ToolboxCommandoImpl.humanReadableByteCountBin;
1111
import static java.util.Objects.requireNonNull;
1212

13+
import com.github.packageurl.PackageURL;
1314
import eu.maveniverse.maven.toolbox.shared.ArtifactMapper;
1415
import eu.maveniverse.maven.toolbox.shared.ArtifactMatcher;
1516
import eu.maveniverse.maven.toolbox.shared.ArtifactNameMapper;
@@ -580,6 +581,7 @@ public static class StatArtifactSink implements Artifacts.Sink {
580581
private final ModuleDescriptorExtractingSink moduleDescriptorExtractingSink;
581582
private final ChecksumArtifactSink checksumArtifactSink;
582583
private final ArtifactUriSink artifactUriSink;
584+
private final ArtifactPurlSink artifactPurlSink;
583585
private final Map<Artifact, BytecodeVersions> bytecodeVersions;
584586

585587
private StatArtifactSink(int level, boolean list, boolean details, Output output, ToolboxCommando tc) {
@@ -590,6 +592,7 @@ private StatArtifactSink(int level, boolean list, boolean details, Output output
590592
this.moduleDescriptorExtractingSink = details ? new ModuleDescriptorExtractingSink(output) : null;
591593
this.checksumArtifactSink = details ? checksumArtifactSink() : null;
592594
this.artifactUriSink = details ? artifactUriSink(output, tc, false) : null;
595+
this.artifactPurlSink = details ? modulePurlSink(output, tc, false) : null;
593596
this.bytecodeVersions = details ? new HashMap<>() : null;
594597
}
595598

@@ -610,6 +613,7 @@ public void accept(Artifact artifact) throws IOException {
610613
moduleDescriptorExtractingSink.accept(artifact);
611614
checksumArtifactSink.accept(artifact);
612615
artifactUriSink.accept(artifact);
616+
artifactPurlSink.accept(artifact);
613617
if (artifact.getFile() != null) {
614618
bytecodeVersions.put(
615619
artifact,
@@ -685,6 +689,10 @@ public void close() throws Exception {
685689
if (artifactUri != null) {
686690
output.tell("{} -- Origin URI: {}", indent, artifactUri.toASCIIString());
687691
}
692+
PackageURL artifactPurl = artifactPurlSink.getPurl(artifact);
693+
if (artifactPurl != null) {
694+
output.tell("{} -- PURL: {}", indent, artifactPurl.canonicalize());
695+
}
688696
}
689697
}
690698
output.tell("{}------------------------------", indent);
@@ -761,4 +769,48 @@ public void close() throws IOException {
761769
}
762770
}
763771
}
772+
773+
/**
774+
* Creates an "Artifact -> PURL" artifact sink.
775+
*/
776+
public static ArtifactPurlSink modulePurlSink(Output output, ToolboxCommando toolboxCommando, boolean dumpOnClose) {
777+
return new ArtifactPurlSink(output, toolboxCommando, dumpOnClose);
778+
}
779+
780+
public static class ArtifactPurlSink implements Artifacts.Sink {
781+
private final Output output;
782+
private final ToolboxCommando toolboxCommando;
783+
private final ConcurrentMap<Artifact, PackageURL> purls = new ConcurrentHashMap<>();
784+
private final boolean dumpOnClose;
785+
786+
public ArtifactPurlSink(Output output, ToolboxCommando toolboxCommando, boolean dumpOnClose) {
787+
this.output = requireNonNull(output, "output");
788+
this.toolboxCommando = requireNonNull(toolboxCommando, "toolboxCommando");
789+
this.dumpOnClose = dumpOnClose;
790+
}
791+
792+
@Override
793+
public void accept(Artifact artifact) throws IOException {
794+
String origin = artifact.getProperty("origin", null);
795+
if (origin != null) {
796+
toolboxCommando
797+
.remoteRepository(origin)
798+
.flatMap(r -> toolboxCommando.artifactPurl(r, artifact))
799+
.ifPresent(uri -> purls.put(artifact, uri));
800+
}
801+
}
802+
803+
public PackageURL getPurl(Artifact artifact) {
804+
return purls.get(artifact);
805+
}
806+
807+
@Override
808+
public void close() throws IOException {
809+
if (dumpOnClose) {
810+
for (Map.Entry<Artifact, PackageURL> entry : purls.entrySet()) {
811+
output.tell("{} -> {}", entry.getKey(), entry.getValue());
812+
}
813+
}
814+
}
815+
}
764816
}

shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/ToolboxCommandoImpl.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import static org.apache.maven.search.api.request.FieldQuery.fieldQuery;
1616
import static org.apache.maven.search.api.request.Query.query;
1717

18+
import com.github.packageurl.MalformedPackageURLException;
19+
import com.github.packageurl.PackageURL;
20+
import com.github.packageurl.PackageURLBuilder;
1821
import eu.maveniverse.domtrip.Document;
1922
import eu.maveniverse.maven.mima.context.Context;
2023
import eu.maveniverse.maven.mima.context.ContextOverrides;
@@ -479,6 +482,27 @@ public Artifact toArtifact(Dependency dependency) {
479482
}
480483
}
481484

485+
@Override
486+
public Optional<PackageURL> artifactPurl(RemoteRepository remoteRepository, Artifact artifact) {
487+
requireNonNull(remoteRepository, "remoteRepository is null");
488+
requireNonNull(artifact, "artifact is null");
489+
PackageURLBuilder builder = PackageURLBuilder.aPackageURL();
490+
builder.withType("maven");
491+
builder.withNamespace(artifact.getGroupId());
492+
builder.withName(artifact.getArtifactId());
493+
builder.withVersion(artifact.getVersion()); // artifact.getBaseVersion() ?
494+
boolean central = "central".equals(remoteRepository.getId());
495+
if (!central) {
496+
builder.withQualifier("repository_url", remoteRepository.getUrl());
497+
}
498+
try {
499+
return Optional.of(builder.build());
500+
} catch (MalformedPackageURLException e) {
501+
output.warn("Malformed PURL", e);
502+
return Optional.empty();
503+
}
504+
}
505+
482506
@Override
483507
public Optional<URI> artifactUri(RemoteRepository remoteRepository, Artifact artifact) {
484508
requireNonNull(remoteRepository, "remoteRepository is null");

0 commit comments

Comments
 (0)