Skip to content

Commit 9bf6561

Browse files
committed
WIP
1 parent d5e34d4 commit 9bf6561

File tree

12 files changed

+276
-18
lines changed

12 files changed

+276
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.toolbox.cli;
9+
10+
import eu.maveniverse.maven.mima.context.Context;
11+
import eu.maveniverse.maven.toolbox.shared.DirectorySink;
12+
import eu.maveniverse.maven.toolbox.shared.ResolutionScope;
13+
import eu.maveniverse.maven.toolbox.shared.ToolboxCommando;
14+
import java.nio.file.Path;
15+
import picocli.CommandLine;
16+
17+
/**
18+
* Resolves transitively a given GAV and copies resulting artifacts to target.
19+
*/
20+
@CommandLine.Command(name = "copyAll", description = "Resolves Maven Artifact and copies them to target")
21+
public final class CopyAll extends ResolverCommandSupport {
22+
@CommandLine.Parameters(index = "0", description = "The GAV to resolve", arity = "1")
23+
private String gav;
24+
25+
@CommandLine.Parameters(index = "1", description = "The target", arity = "1")
26+
private Path target;
27+
28+
@CommandLine.Option(
29+
names = {"--resolutionScope"},
30+
defaultValue = "runtime",
31+
description = "Resolution scope to resolve (default main-runtime)")
32+
private String resolutionScope;
33+
34+
@CommandLine.Option(
35+
names = {"--boms"},
36+
defaultValue = "",
37+
split = ",",
38+
description = "Comma separated list of BOMs to apply")
39+
private java.util.List<String> boms;
40+
41+
@Override
42+
protected boolean doCall(Context context) throws Exception {
43+
Path targetPath = target.toAbsolutePath();
44+
ToolboxCommando toolboxCommando = ToolboxCommando.getOrCreate(context);
45+
return toolboxCommando.copyAll(
46+
ResolutionScope.parse(resolutionScope),
47+
toolboxCommando.toolboxResolver().loadGav(gav, boms),
48+
DirectorySink.flat(output, targetPath),
49+
output);
50+
}
51+
}

cli/src/main/java/eu/maveniverse/maven/toolbox/cli/Main.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@
1717
name = "toolbox",
1818
subcommands = {
1919
Classpath.class,
20+
CopyAll.class,
2021
Deploy.class,
2122
DeployRecorded.class,
2223
Dump.class,
2324
Exists.class,
2425
Identify.class,
2526
Install.class,
2627
List.class,
27-
ListRepositories.class,
2828
ListAvailablePlugins.class,
29-
Search.class,
29+
ListRepositories.class,
3030
Record.class,
3131
Repl.class,
3232
Resolve.class,
33+
Search.class,
3334
Tree.class,
3435
Test.class,
3536
Verify.class

foo/hamcrest-core-1.3.jar

44 KB
Binary file not shown.

foo/junit-4.13.2.jar

376 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.toolbox.shared;
9+
10+
import static java.util.Objects.requireNonNull;
11+
12+
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactMapper;
13+
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactMatcher;
14+
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactNameMapper;
15+
import java.io.IOException;
16+
import java.io.UncheckedIOException;
17+
import java.nio.file.Files;
18+
import java.nio.file.Path;
19+
import java.nio.file.StandardCopyOption;
20+
import java.util.Collection;
21+
import java.util.HashSet;
22+
import java.util.function.Consumer;
23+
import org.eclipse.aether.artifact.Artifact;
24+
25+
/**
26+
* Construction to accept collection of artifacts, for example like a filesystem directory.
27+
*/
28+
public final class DirectorySink implements Consumer<Collection<Artifact>> {
29+
30+
public static DirectorySink flat(Output output, Path path) throws IOException {
31+
return new DirectorySink(output, path);
32+
}
33+
34+
private final Output output;
35+
private final Path directory;
36+
private final ArtifactMatcher artifactMatcher;
37+
private final ArtifactMapper artifactMapper;
38+
private final ArtifactNameMapper artifactNameMapper;
39+
private final boolean allowOverwrite;
40+
private final HashSet<Path> writtenPaths;
41+
42+
private DirectorySink(Output output, Path directory) throws IOException {
43+
this.output = requireNonNull(output, "output");
44+
this.directory = requireNonNull(directory, "directory");
45+
if (Files.exists(directory) && !Files.isDirectory(directory)) {
46+
throw new IllegalArgumentException("directory must not exists, or be a directory");
47+
}
48+
if (!Files.exists(directory)) {
49+
Files.createDirectories(directory);
50+
}
51+
52+
this.artifactMatcher = ArtifactMatcher.any();
53+
this.artifactMapper = ArtifactMapper.identity();
54+
this.artifactNameMapper = ArtifactNameMapper.ACVE();
55+
this.allowOverwrite = false;
56+
this.writtenPaths = new HashSet<>();
57+
}
58+
59+
@Override
60+
public void accept(Collection<Artifact> artifacts) {
61+
requireNonNull(artifacts, "artifacts");
62+
output.verbose("Copying {} artifacts to directory {}", artifacts.size(), directory);
63+
try {
64+
for (Artifact artifact : artifacts) {
65+
accept(artifact);
66+
}
67+
} catch (IOException e) {
68+
cleanup(artifacts, e);
69+
}
70+
}
71+
72+
private void accept(Artifact artifact) throws IOException {
73+
output.verbose("Artifact {} processed", artifact);
74+
if (artifactMatcher.test(artifact)) {
75+
output.verbose(" matched");
76+
String name = artifactNameMapper.map(artifactMapper.map(artifact));
77+
output.verbose(" mapped to name {}", name);
78+
Path target = directory.resolve(name);
79+
if (!writtenPaths.add(target) && !allowOverwrite) {
80+
throw new IOException("Overwrite prevented: check mappings");
81+
}
82+
output.verbose(" copied to file {}", target);
83+
Files.copy(
84+
artifact.getFile().toPath(),
85+
target,
86+
StandardCopyOption.REPLACE_EXISTING,
87+
StandardCopyOption.COPY_ATTRIBUTES);
88+
}
89+
}
90+
91+
private void cleanup(Collection<Artifact> artifacts, IOException e) {
92+
output.error("IO error happened, cleaning up", e);
93+
writtenPaths.forEach(p -> {
94+
try {
95+
Files.deleteIfExists(p);
96+
} catch (IOException ex) {
97+
// ignore
98+
}
99+
});
100+
throw new UncheckedIOException(e);
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.toolbox.shared;
9+
10+
/**
11+
* Null Output.
12+
*/
13+
public final class NullOutput implements Output {
14+
public NullOutput() {}
15+
16+
@Override
17+
public boolean isVerbose() {
18+
return false;
19+
}
20+
21+
@Override
22+
public void verbose(String msg, Object... params) {}
23+
24+
@Override
25+
public void normal(String msg, Object... params) {}
26+
27+
@Override
28+
public void warn(String msg, Object... params) {}
29+
30+
@Override
31+
public void error(String msg, Object... params) {}
32+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import org.slf4j.Logger;
1313

1414
/**
15-
* Output.
15+
* Slf4j Logger backed Output.
1616
*/
1717
public final class Slf4jOutput implements Output {
1818
private final Logger logger;

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

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.io.Closeable;
1616
import java.io.IOException;
1717
import java.util.Collection;
18+
import java.util.function.Consumer;
1819
import java.util.function.Supplier;
1920
import org.eclipse.aether.artifact.Artifact;
2021
import org.eclipse.aether.repository.RemoteRepository;
@@ -71,6 +72,12 @@ static void unset(Context context) {
7172

7273
boolean classpath(ResolutionScope resolutionScope, ResolutionRoot resolutionRoot, Output output);
7374

75+
boolean copyAll(
76+
ResolutionScope resolutionScope,
77+
ResolutionRoot resolutionRoot,
78+
Consumer<Collection<Artifact>> consumer,
79+
Output output);
80+
7481
boolean deploy(String remoteRepositorySpec, Supplier<Collection<Artifact>> artifactSupplier, Output output);
7582

7683
boolean deployAllRecorded(String remoteRepositorySpec, boolean stopRecording, Output output);

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,16 @@ public Artifact map(Artifact artifact) {
3131
};
3232
}
3333

34-
static ArtifactMapper bV() {
34+
static ArtifactMapper identity() {
35+
return new ArtifactMapper() {
36+
@Override
37+
public Artifact map(Artifact artifact) {
38+
return artifact;
39+
}
40+
};
41+
}
42+
43+
static ArtifactMapper baseVersion() {
3544
return new ArtifactMapper() {
3645
@Override
3746
public Artifact map(Artifact artifact) {
@@ -47,7 +56,7 @@ public Artifact map(Artifact artifact) {
4756
};
4857
}
4958

50-
static ArtifactMapper woC() {
59+
static ArtifactMapper omitClassifier() {
5160
return new ArtifactMapper() {
5261
@Override
5362
public Artifact map(Artifact artifact) {

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

+17-13
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ public boolean test(Artifact artifact) {
6666
};
6767
}
6868

69+
static ArtifactMatcher withoutClassifier() {
70+
return a -> a.getClassifier() == null || a.getClassifier().trim().isEmpty();
71+
}
72+
73+
static ArtifactMatcher artifact(String coordinate) {
74+
Artifact prototype = parsePrototype(coordinate);
75+
return a -> matches(prototype.getGroupId(), a.getGroupId())
76+
&& matches(prototype.getArtifactId(), a.getArtifactId())
77+
&& matches(prototype.getVersion(), a.getVersion())
78+
&& matches(prototype.getExtension(), a.getExtension())
79+
&& matches(prototype.getClassifier(), a.getClassifier());
80+
}
81+
82+
static ArtifactMatcher any() {
83+
return a -> true;
84+
}
85+
6986
private static boolean isAny(String str) {
7087
return "*".equals(str);
7188
}
@@ -108,17 +125,4 @@ private static Artifact parsePrototype(String coordinate) {
108125
}
109126
return s;
110127
}
111-
112-
static Predicate<Artifact> withoutClassifier() {
113-
return a -> a.getClassifier() == null || a.getClassifier().trim().isEmpty();
114-
}
115-
116-
static Predicate<Artifact> artifactPredicate(String coordinate) {
117-
Artifact prototype = parsePrototype(coordinate);
118-
return a -> matches(prototype.getGroupId(), a.getGroupId())
119-
&& matches(prototype.getArtifactId(), a.getArtifactId())
120-
&& matches(prototype.getVersion(), a.getVersion())
121-
&& matches(prototype.getExtension(), a.getExtension())
122-
&& matches(prototype.getClassifier(), a.getClassifier());
123-
}
124128
}

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

+29
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,40 @@
77
*/
88
package eu.maveniverse.maven.toolbox.shared.internal;
99

10+
import static java.util.Objects.requireNonNull;
11+
12+
import java.util.Arrays;
13+
import java.util.Collection;
1014
import org.eclipse.aether.artifact.Artifact;
1115

1216
public interface ArtifactNameMapper {
1317
String map(Artifact artifact);
1418

19+
static ArtifactNameMapper compose(ArtifactNameMapper... mappers) {
20+
return compose(Arrays.asList(mappers));
21+
}
22+
23+
static ArtifactNameMapper compose(Collection<ArtifactNameMapper> mappers) {
24+
return new ArtifactNameMapper() {
25+
@Override
26+
public String map(Artifact artifact) {
27+
String result = "";
28+
for (ArtifactNameMapper mapper : mappers) {
29+
result += mapper.map(artifact);
30+
}
31+
return result;
32+
}
33+
};
34+
}
35+
36+
static ArtifactNameMapper prefix(String prefix) {
37+
requireNonNull(prefix, "prefix");
38+
if (prefix.trim().isEmpty()) {
39+
throw new IllegalArgumentException("invalid prefix");
40+
}
41+
return artifact -> prefix;
42+
}
43+
1544
static ArtifactNameMapper GACVE() {
1645
return artifact -> {
1746
String result = artifact.getGroupId() + ".";

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

+23
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Map;
4040
import java.util.Objects;
4141
import java.util.Properties;
42+
import java.util.function.Consumer;
4243
import java.util.function.Predicate;
4344
import java.util.function.Supplier;
4445
import java.util.stream.Collectors;
@@ -138,6 +139,28 @@ public boolean classpath(ResolutionScope resolutionScope, ResolutionRoot resolut
138139
}
139140
}
140141

142+
@Override
143+
public boolean copyAll(
144+
ResolutionScope resolutionScope,
145+
ResolutionRoot resolutionRoot,
146+
Consumer<Collection<Artifact>> consumer,
147+
Output output) {
148+
try {
149+
DependencyResult dependencyResult = toolboxResolver.resolve(
150+
resolutionScope,
151+
resolutionRoot.getArtifact(),
152+
resolutionRoot.getDependencies(),
153+
resolutionRoot.getManagedDependencies());
154+
155+
consumer.accept(dependencyResult.getArtifactResults().stream()
156+
.map(ArtifactResult::getArtifact)
157+
.collect(Collectors.toList()));
158+
return true;
159+
} catch (Exception e) {
160+
throw new RuntimeException(e);
161+
}
162+
}
163+
141164
@Override
142165
public boolean deploy(String remoteRepositorySpec, Supplier<Collection<Artifact>> artifactSupplier, Output output) {
143166
try {

0 commit comments

Comments
 (0)