Skip to content

Commit

Permalink
Add more sinks
Browse files Browse the repository at this point in the history
  • Loading branch information
cstamas committed Mar 22, 2024
1 parent e82c104 commit 2880755
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@
/**
* Construction to accept collection of artifacts, for example like a filesystem directory.
*/
public interface ArtifactSink {
public interface ArtifactSink extends AutoCloseable {
default void accept(Collection<Artifact> artifacts) throws IOException {
requireNonNull(artifacts, "artifacts");
for (Artifact artifact : artifacts) {
accept(artifact);
try {
for (Artifact artifact : artifacts) {
accept(artifact);
}
} catch (IOException e) {
cleanup(e);
throw e;
}
}

void accept(Artifact artifact) throws IOException;

default void cleanup(IOException e) {}

default void close() throws Exception {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,5 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(classifier, extension);
}

@Override
public String toString() {
return (classifier == null ? "" : classifier) + "." + extension;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2023-2024 Maveniverse Org.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*/
package eu.maveniverse.maven.toolbox.shared;

import static java.util.Objects.requireNonNull;

import java.util.Collection;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
import org.eclipse.aether.repository.RemoteRepository;

/**
* Construction to accept collection of artifacts and deploy them into given remote repository.
*/
public final class DeployingSink implements ArtifactSink {
/**
* Creates installing sink that installs into passed in session local repository.
*/
public static DeployingSink deploying(
Output output, RepositorySystem system, RepositorySystemSession session, RemoteRepository repository) {
return new DeployingSink(output, system, session, repository);
}

private final Output output;
private final RepositorySystem system;
private final RepositorySystemSession session;
private final DeployRequest deployRequest;

private DeployingSink(
Output output, RepositorySystem system, RepositorySystemSession session, RemoteRepository repository) {
this.output = requireNonNull(output, "output");
this.system = requireNonNull(system, "system");
this.session = requireNonNull(session, "session");
this.deployRequest = new DeployRequest();
this.deployRequest.setRepository(repository);
this.deployRequest.setTrace(RequestTrace.newChild(null, this));
}

@Override
public void accept(Collection<Artifact> artifacts) {
requireNonNull(artifacts, "artifacts");
deployRequest.setArtifacts(artifacts);
}

@Override
public void accept(Artifact artifact) {
requireNonNull(artifact, "artifact");
deployRequest.addArtifact(artifact);
}

@Override
public void close() throws DeploymentException {
output.normal(
"Deploying {} artifacts to {}...", deployRequest.getArtifacts().size(), deployRequest.getRepository());
system.deploy(session, deployRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactMapper;
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactMatcher;
import eu.maveniverse.maven.toolbox.shared.internal.ArtifactNameMapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.HashSet;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -27,30 +27,57 @@
*/
public final class DirectorySink implements ArtifactSink {
/**
* Creates plain "flat" directory sink that accepts all artifacts and writes them out having filenames as
* Creates plain "flat" directory sink, that accepts all artifacts and copies them out having filenames as
* "A[-C]-V.E" and prevents overwrite (what you usually want).
* <p>
* This means that if your set of artifacts have artifacts with different groupIDs but same artifactIDs, this sink
* will fail while accepting them, to prevent overwrite. Duplicated artifacts are filtered out.
*/
public static DirectorySink flat(Output output, Path path) throws IOException {
return new DirectorySink(output, path, ArtifactMatcher.unique(), a -> a, ArtifactNameMapper.ACVE(), false);
return new DirectorySink(
output, path, Mode.COPY, ArtifactMatcher.unique(), a -> a, ArtifactNameMapper.ACVE(), false);
}

/**
* Creates "repository" directory sink, that accepts all artifacts and copies them out having filenames as
* a "remote repository" (usable as file based remote repository, or can be published via HTTP). It also
* prevents overwrite (what you usually want). This repository may ve handy for testing, but does not serve
* as interchangeable solution of installing or deploying artifacts for real.
*/
public static DirectorySink repository(Output output, Path path) throws IOException {
return new DirectorySink(
output,
path,
Mode.COPY,
ArtifactMatcher.unique(),
a -> a,
ArtifactNameMapper.repositoryDefault(File.separator),
false);
}

public enum Mode {
COPY,
LINK,
SYMLINK
}

private final Output output;
private final Path directory;
private final boolean directoryCreated;
private final Mode mode;
private final Predicate<Artifact> artifactMatcher;
private final Function<Artifact, Artifact> artifactMapper;
private final Function<Artifact, String> artifactNameMapper;
private final boolean allowOverwrite;
private final HashSet<Path> writtenPaths;
private final StandardCopyOption[] copyFlags;

/**
* Creates a directory sink.
*
* @param output The output.
* @param directory The directory, if not existing, will be created.
* @param mode The accepting mode: copy, link or symlink.
* @param artifactMatcher The matcher, that decides is this sink accepting artifact or not.
* @param artifactMapper The artifact mapper, that may re-map artifact.
* @param artifactNameMapper The artifact name mapper, that decides what file name will be of the artifact.
Expand All @@ -61,13 +88,15 @@ public static DirectorySink flat(Output output, Path path) throws IOException {
private DirectorySink(
Output output,
Path directory,
Mode mode,
ArtifactMatcher artifactMatcher,
ArtifactMapper artifactMapper,
ArtifactNameMapper artifactNameMapper,
boolean allowOverwrite)
throws IOException {
this.output = requireNonNull(output, "output");
this.directory = requireNonNull(directory, "directory").toAbsolutePath();
this.mode = requireNonNull(mode, "mode");
if (Files.exists(directory) && !Files.isDirectory(directory)) {
throw new IllegalArgumentException("directory must not exists, or must be a directory");
}
Expand All @@ -83,24 +112,14 @@ private DirectorySink(
this.artifactNameMapper = requireNonNull(artifactNameMapper, "artifactNameMapper");
this.allowOverwrite = allowOverwrite;
this.writtenPaths = new HashSet<>();
}

@Override
public void accept(Collection<Artifact> artifacts) throws IOException {
requireNonNull(artifacts, "artifacts");
output.verbose("Copying {} artifacts to directory {}", artifacts.size(), directory);
try {
for (Artifact artifact : artifacts) {
accept(artifact);
}
} catch (IOException e) {
cleanup();
throw e;
}
this.copyFlags = allowOverwrite
? new StandardCopyOption[] {StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES}
: new StandardCopyOption[] {StandardCopyOption.COPY_ATTRIBUTES};
}

@Override
public void accept(Artifact artifact) throws IOException {
requireNonNull(artifact, "artifact");
output.verbose("Accept artifact {}", artifact);
if (artifactMatcher.test(artifact)) {
output.verbose(" matched");
Expand All @@ -113,18 +132,29 @@ public void accept(Artifact artifact) throws IOException {
if (!writtenPaths.add(target) && !allowOverwrite) {
throw new IOException("Overwrite prevented; check mappings");
}
output.verbose(" copied to file {}", target);
Files.copy(
artifact.getFile().toPath(),
target,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
switch (mode) {
case COPY:
output.verbose(" copied to file {}", target);
Files.copy(artifact.getFile().toPath(), target, copyFlags);
break;
case LINK:
output.verbose(" linked to file {}", target);
Files.createLink(target, artifact.getFile().toPath());
break;
case SYMLINK:
output.verbose(" symlinked to file {}", target);
Files.createSymbolicLink(target, artifact.getFile().toPath());
break;
default:
throw new IllegalArgumentException("unknown mode");
}
} else {
output.verbose(" not matched");
}
}

private void cleanup() {
@Override
public void cleanup(IOException e) {
output.error("Cleaning up: {}", directory);
writtenPaths.forEach(p -> {
try {
Expand All @@ -136,7 +166,7 @@ private void cleanup() {
if (directoryCreated) {
try {
Files.deleteIfExists(directory);
} catch (IOException e) {
} catch (IOException ex) {
// ignore
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023-2024 Maveniverse Org.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*/
package eu.maveniverse.maven.toolbox.shared;

import static java.util.Objects.requireNonNull;

import java.util.Collection;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallationException;

/**
* Construction to accept collection of artifacts and install them into local repository.
*/
public final class InstallingSink implements ArtifactSink {
/**
* Creates installing sink that installs into passed in session local repository.
*/
public static InstallingSink installing(Output output, RepositorySystem system, RepositorySystemSession session) {
return new InstallingSink(output, system, session);
}

private final Output output;
private final RepositorySystem system;
private final RepositorySystemSession session;
private final InstallRequest installRequest;

private InstallingSink(Output output, RepositorySystem system, RepositorySystemSession session) {
this.output = requireNonNull(output, "output");
this.system = requireNonNull(system, "system");
this.session = requireNonNull(session, "session");
this.installRequest = new InstallRequest();
this.installRequest.setTrace(RequestTrace.newChild(null, this));
}

@Override
public void accept(Collection<Artifact> artifacts) {
requireNonNull(artifacts, "artifacts");
installRequest.setArtifacts(artifacts);
}

@Override
public void accept(Artifact artifact) {
requireNonNull(artifact, "artifact");
installRequest.addArtifact(artifact);
}

@Override
public void close() throws InstallationException {
output.normal(
"Installing {} artifacts...", installRequest.getArtifacts().size());
system.install(session, installRequest);
}
}
Loading

0 comments on commit 2880755

Please sign in to comment.