From de10d1c5f95b45b24684d9652816fe41b87a1aef Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 1 Oct 2025 14:01:25 +0200 Subject: [PATCH] Stop proliferation of static helper IO methods Originall, the PathProcessor (before FileProcessor) was main component dealing with IO, restore this state. The proliferation of static IO helpers is just bad, as it does not allow (easy) overriding as it is the case of components. --- .../basic/BasicRepositoryConnector.java | 10 +- .../BasicRepositoryConnectorFactory.java | 6 + .../connector/basic/ChecksumValidator.java | 9 +- .../basic/ChecksumValidatorTest.java | 2 + .../SigstoreSignatureArtifactGenerator.java | 11 +- ...toreSignatureArtifactGeneratorFactory.java | 8 +- .../sigstore/SigstoreSignerFactoryTest.java | 3 +- .../internal/impl/DefaultPathProcessor.java | 90 +------ .../SummaryFileTrustedChecksumsSource.java | 23 +- .../GroupIdRemoteRepositoryFilterSource.java | 12 +- .../impl/DefaultArtifactResolverTest.java | 7 +- .../internal/impl/DefaultDeployerTest.java | 4 +- .../internal/impl/DefaultInstallerTest.java | 7 +- ...SummaryFileTrustedChecksumsSourceTest.java | 4 +- ...oupIdRemoteRepositoryFilterSourceTest.java | 5 +- .../eclipse/aether/spi/io/PathProcessor.java | 88 +++++- .../aether/spi/io/PathProcessorSupport.java | 253 ++++++++++++++++++ .../supplier/RepositorySystemSupplier.java | 6 +- .../supplier/RepositorySystemSupplier.java | 6 +- .../internal/test/util/TestPathProcessor.java | 44 +-- .../transport/apache/ApacheTransporter.java | 3 +- .../apache/ApacheTransporterTest.java | 4 +- .../aether/transport/jdk/JdkTransporter.java | 5 +- .../transport/jetty/JettyTransporter.java | 3 +- .../transport/jetty/JettyTransporterTest.java | 4 +- .../transport/minio/MinioTransporter.java | 14 +- .../minio/MinioTransporterFactory.java | 9 +- .../minio/MinioTransporterFactoryTest.java | 3 +- .../transport/minio/MinioTransporterIT.java | 23 +- maven-resolver-transport-wagon/pom.xml | 5 + .../transport/wagon/WagonTransporter.java | 30 ++- .../wagon/WagonTransporterFactory.java | 9 +- .../wagon/AbstractWagonTransporterTest.java | 4 +- .../org/eclipse/aether/util/FileUtils.java | 3 + 34 files changed, 512 insertions(+), 205 deletions(-) create mode 100644 maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessorSupport.java diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java index 6bf71e41d..fd2d51e93 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java @@ -54,13 +54,13 @@ import org.eclipse.aether.spi.connector.transport.Transporter; import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.NoRepositoryConnectorException; import org.eclipse.aether.transfer.NoRepositoryLayoutException; import org.eclipse.aether.transfer.TransferEvent; import org.eclipse.aether.transfer.TransferResource; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.eclipse.aether.util.concurrency.RunnableErrorForwarder; import org.eclipse.aether.util.concurrency.SmartExecutor; import org.eclipse.aether.util.concurrency.SmartExecutorUtils; @@ -87,6 +87,8 @@ final class BasicRepositoryConnector implements RepositoryConnector { private final Map providedChecksumsSources; + private final PathProcessor pathProcessor; + private final ChecksumProcessor checksumProcessor; private final RemoteRepository repository; @@ -113,12 +115,14 @@ final class BasicRepositoryConnector implements RepositoryConnector { private final AtomicBoolean closed; + @SuppressWarnings("checkstyle:parameternumber") BasicRepositoryConnector( RepositorySystemSession session, RemoteRepository repository, TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, ChecksumPolicyProvider checksumPolicyProvider, + PathProcessor pathProcessor, ChecksumProcessor checksumProcessor, Map providedChecksumsSources) throws NoRepositoryConnectorException { @@ -140,6 +144,7 @@ final class BasicRepositoryConnector implements RepositoryConnector { this.session = session; this.repository = repository; this.checksumProcessor = checksumProcessor; + this.pathProcessor = pathProcessor; this.providedChecksumsSources = providedChecksumsSources; this.executors = new ConcurrentHashMap<>(); this.closed = new AtomicBoolean(false); @@ -496,6 +501,7 @@ class GetTaskRunner extends TaskRunner implements ChecksumValidator.ChecksumFetc checksumValidator = new ChecksumValidator( file, checksumAlgorithmFactories, + pathProcessor, checksumProcessor, this, checksumPolicy, @@ -518,7 +524,7 @@ public boolean fetchChecksum(URI remote, Path local) throws Exception { @Override protected void runTask() throws Exception { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(file)) { + try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(file)) { final Path tmp = tempFile.getPath(); listener.setChecksumCalculator(checksumValidator.newChecksumCalculator(tmp)); for (int firstTrial = 0, lastTrial = 1, trial = firstTrial; ; trial++) { diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java index b16610e87..9cd0f1ab7 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java @@ -32,6 +32,7 @@ import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoRepositoryConnectorException; import static java.util.Objects.requireNonNull; @@ -50,6 +51,8 @@ public final class BasicRepositoryConnectorFactory implements RepositoryConnecto private final ChecksumPolicyProvider checksumPolicyProvider; + private final PathProcessor pathProcessor; + private final ChecksumProcessor checksumProcessor; private final Map providedChecksumsSources; @@ -61,11 +64,13 @@ public BasicRepositoryConnectorFactory( TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, ChecksumPolicyProvider checksumPolicyProvider, + PathProcessor pathProcessor, ChecksumProcessor checksumProcessor, Map providedChecksumsSources) { this.transporterProvider = requireNonNull(transporterProvider, "transporter provider cannot be null"); this.layoutProvider = requireNonNull(layoutProvider, "repository layout provider cannot be null"); this.checksumPolicyProvider = requireNonNull(checksumPolicyProvider, "checksum policy provider cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); this.checksumProcessor = requireNonNull(checksumProcessor, "checksum processor cannot be null"); this.providedChecksumsSources = requireNonNull(providedChecksumsSources, "provided checksum sources cannot be null"); @@ -99,6 +104,7 @@ public RepositoryConnector newInstance(RepositorySystemSession session, RemoteRe transporterProvider, layoutProvider, checksumPolicyProvider, + pathProcessor, checksumProcessor, providedChecksumsSources); } diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java index 24fdbc3a1..850474f0a 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java @@ -31,8 +31,8 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation; import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.ChecksumFailureException; -import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,6 +56,8 @@ interface ChecksumFetcher { private final Collection checksumAlgorithmFactories; + private final PathProcessor pathProcessor; + private final ChecksumProcessor checksumProcessor; private final ChecksumFetcher checksumFetcher; @@ -68,9 +70,11 @@ interface ChecksumFetcher { private final Map checksumExpectedValues; + @SuppressWarnings("checkstyle:parameternumber") ChecksumValidator( Path dataPath, Collection checksumAlgorithmFactories, + PathProcessor pathProcessor, ChecksumProcessor checksumProcessor, ChecksumFetcher checksumFetcher, ChecksumPolicy checksumPolicy, @@ -78,6 +82,7 @@ interface ChecksumFetcher { Collection checksumLocations) { this.dataPath = dataPath; this.checksumAlgorithmFactories = checksumAlgorithmFactories; + this.pathProcessor = pathProcessor; this.checksumProcessor = checksumProcessor; this.checksumFetcher = checksumFetcher; this.checksumPolicy = checksumPolicy; @@ -156,7 +161,7 @@ private boolean validateExternalChecksums(Map actualChecksums) throws continue; } Path checksumFile = getChecksumPath(checksumLocation.getChecksumAlgorithmFactory()); - try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { + try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) { Path tmp = tempFile.getPath(); try { if (!checksumFetcher.fetchChecksum(checksumLocation.getLocation(), tmp)) { diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java index 01c3f9ae5..ce6748c79 100644 --- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java +++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java @@ -36,6 +36,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.spi.connector.layout.RepositoryLayout; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.ChecksumFailureException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -189,6 +190,7 @@ private ChecksumValidator newValidator(Map providedChecksums, St return new ChecksumValidator( dataFile.toPath(), checksumAlgorithmFactories, + new PathProcessorSupport(), new TestChecksumProcessor(), fetcher, policy, diff --git a/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGenerator.java b/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGenerator.java index 7e7ca6712..e5c8ae295 100644 --- a/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGenerator.java +++ b/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGenerator.java @@ -37,7 +37,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.generator.sigstore.internal.FulcioOidHelper; import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; -import org.eclipse.aether.util.FileUtils; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.util.artifact.SubArtifact; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,13 +45,18 @@ final class SigstoreSignatureArtifactGenerator implements ArtifactGenerator { private static final String ARTIFACT_EXTENSION = ".sigstore.json"; private final Logger logger = LoggerFactory.getLogger(getClass()); + private final PathProcessor pathProcessor; private final ArrayList artifacts; private final Predicate signableArtifactPredicate; private final boolean publicStaging; private final ArrayList signatureTempFiles; SigstoreSignatureArtifactGenerator( - Collection artifacts, Predicate signableArtifactPredicate, boolean publicStaging) { + PathProcessor pathProcessor, + Collection artifacts, + Predicate signableArtifactPredicate, + boolean publicStaging) { + this.pathProcessor = pathProcessor; this.artifacts = new ArrayList<>(artifacts); this.signableArtifactPredicate = signableArtifactPredicate; this.publicStaging = publicStaging; @@ -107,7 +112,7 @@ public Collection generate(Collection ge + FulcioOidHelper.getIssuerV2(cert) + " IdP)"); - FileUtils.writeFile(signatureTempFile, p -> Files.writeString(p, bundle.toJson())); + pathProcessor.write(signatureTempFile, bundle.toJson()); long duration = System.currentTimeMillis() - start; logger.debug(" > Rekor entry " diff --git a/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGeneratorFactory.java b/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGeneratorFactory.java index c512eeb48..bb51ec20c 100644 --- a/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGeneratorFactory.java +++ b/maven-resolver-generator-sigstore/src/main/java/org/eclipse/aether/generator/sigstore/SigstoreSignatureArtifactGeneratorFactory.java @@ -31,6 +31,7 @@ import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory; import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.util.ConfigUtils; @Singleton @@ -40,10 +41,13 @@ public final class SigstoreSignatureArtifactGeneratorFactory implements Artifact public static final String NAME = "sigstore"; private final ArtifactPredicateFactory artifactPredicateFactory; + private final PathProcessor pathProcessor; @Inject - public SigstoreSignatureArtifactGeneratorFactory(ArtifactPredicateFactory artifactPredicateFactory) { + public SigstoreSignatureArtifactGeneratorFactory( + ArtifactPredicateFactory artifactPredicateFactory, PathProcessor pathProcessor) { this.artifactPredicateFactory = artifactPredicateFactory; + this.pathProcessor = pathProcessor; } @Override @@ -68,7 +72,7 @@ private ArtifactGenerator newInstance(RepositorySystemSession session, Collectio SigstoreConfigurationKeys.CONFIG_PROP_PUBLIC_STAGING); return new SigstoreSignatureArtifactGenerator( - artifacts, artifactPredicateFactory.newInstance(session)::hasChecksums, publicStaging); + pathProcessor, artifacts, artifactPredicateFactory.newInstance(session)::hasChecksums, publicStaging); } @Override diff --git a/maven-resolver-generator-sigstore/src/test/java/org/eclipse/aether/generator/sigstore/SigstoreSignerFactoryTest.java b/maven-resolver-generator-sigstore/src/test/java/org/eclipse/aether/generator/sigstore/SigstoreSignerFactoryTest.java index b8f6badac..30423db6d 100644 --- a/maven-resolver-generator-sigstore/src/test/java/org/eclipse/aether/generator/sigstore/SigstoreSignerFactoryTest.java +++ b/maven-resolver-generator-sigstore/src/test/java/org/eclipse/aether/generator/sigstore/SigstoreSignerFactoryTest.java @@ -34,6 +34,7 @@ import org.eclipse.aether.spi.artifact.ArtifactPredicate; import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory; import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ private SigstoreSignatureArtifactGeneratorFactory createFactory() throws Excepti when(artifactPredicateFactory.newInstance(any(RepositorySystemSession.class))) .thenReturn(artifactPredicate); - return new SigstoreSignatureArtifactGeneratorFactory(artifactPredicateFactory); + return new SigstoreSignatureArtifactGeneratorFactory(artifactPredicateFactory, new PathProcessorSupport()); } private RepositorySystemSession createSession() { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java index f27d85382..87e22befb 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java @@ -21,95 +21,11 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.AccessDeniedException; -import java.nio.file.FileSystemException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.attribute.FileTime; - -import org.eclipse.aether.spi.io.PathProcessor; -import org.eclipse.aether.util.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.aether.spi.io.PathProcessorSupport; /** - * A utility class helping with file-based operations. + * Exposing path processor (from SPI). */ @Singleton @Named -public class DefaultPathProcessor implements PathProcessor { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @Override - public void setLastModified(Path path, long value) throws IOException { - try { - Files.setLastModifiedTime(path, FileTime.fromMillis(value)); - } catch (FileSystemException e) { - // MRESOLVER-536: Java uses generic FileSystemException for some weird cases, - // but some subclasses like AccessDeniedEx should be re-thrown - if (e instanceof AccessDeniedException) { - throw e; - } - logger.trace("Failed to set last modified date: {}", path, e); - } - } - - @Override - public void write(Path target, String data) throws IOException { - FileUtils.writeFile(target, p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8))); - } - - @Override - public void write(Path target, InputStream source) throws IOException { - FileUtils.writeFile(target, p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING)); - } - - @Override - public long copy(Path source, Path target, ProgressListener listener) throws IOException { - try (InputStream in = new BufferedInputStream(Files.newInputStream(source)); - FileUtils.CollocatedTempFile tempTarget = FileUtils.newTempFile(target); - OutputStream out = new BufferedOutputStream(Files.newOutputStream(tempTarget.getPath()))) { - long result = copy(out, in, listener); - tempTarget.move(); - return result; - } - } - - private long copy(OutputStream os, InputStream is, ProgressListener listener) throws IOException { - long total = 0L; - byte[] buffer = new byte[1024 * 32]; - while (true) { - int bytes = is.read(buffer); - if (bytes < 0) { - break; - } - - os.write(buffer, 0, bytes); - - total += bytes; - - if (listener != null && bytes > 0) { - try { - listener.progressed(ByteBuffer.wrap(buffer, 0, bytes)); - } catch (Exception e) { - // too bad - } - } - } - - return total; - } - - @Override - public void move(Path source, Path target) throws IOException { - Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); - } -} +public class DefaultPathProcessor extends PathProcessorSupport {} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index ec629e7ae..85c9be19f 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; +import java.util.stream.Collectors; import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; @@ -44,14 +45,13 @@ import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; /** * Compact file {@link FileTrustedChecksumsSourceSupport} implementation that use specified directory as base @@ -132,6 +132,8 @@ public final class SummaryFileTrustedChecksumsSource extends FileTrustedChecksum private final RepositorySystemLifecycle repositorySystemLifecycle; + private final PathProcessor pathProcessor; + private final ConcurrentHashMap> checksums; private final ConcurrentHashMap changedChecksums; @@ -140,9 +142,12 @@ public final class SummaryFileTrustedChecksumsSource extends FileTrustedChecksum @Inject public SummaryFileTrustedChecksumsSource( - LocalPathComposer localPathComposer, RepositorySystemLifecycle repositorySystemLifecycle) { + LocalPathComposer localPathComposer, + RepositorySystemLifecycle repositorySystemLifecycle, + PathProcessor pathProcessor) { this.localPathComposer = requireNonNull(localPathComposer); this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); + this.pathProcessor = requireNonNull(pathProcessor); this.checksums = new ConcurrentHashMap<>(); this.changedChecksums = new ConcurrentHashMap<>(); this.onShutdownHandlerRegistered = new AtomicBoolean(false); @@ -325,14 +330,12 @@ private void saveRecordedLines() { result.putAll(recordedLines); LOGGER.info("Saving {} checksums to '{}'", result.size(), summaryFile); - FileUtils.writeFileWithBackup( + pathProcessor.writeWithBackup( summaryFile, - p -> Files.write( - p, - result.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map(e -> e.getValue() + " " + e.getKey()) - .collect(toList()))); + result.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> e.getValue() + " " + e.getKey()) + .collect(Collectors.joining(System.lineSeparator()))); } catch (IOException e) { exceptions.add(e); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java index 255e6f0a9..92585d35f 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java @@ -34,6 +34,7 @@ import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.aether.MultiRuntimeException; @@ -45,9 +46,9 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -136,6 +137,8 @@ public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryF private final RepositorySystemLifecycle repositorySystemLifecycle; + private final PathProcessor pathProcessor; + private final ConcurrentHashMap rules; private final ConcurrentHashMap ruleFiles; @@ -145,8 +148,10 @@ public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryF private final AtomicBoolean onShutdownHandlerRegistered; @Inject - public GroupIdRemoteRepositoryFilterSource(RepositorySystemLifecycle repositorySystemLifecycle) { + public GroupIdRemoteRepositoryFilterSource( + RepositorySystemLifecycle repositorySystemLifecycle, PathProcessor pathProcessor) { this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); + this.pathProcessor = requireNonNull(pathProcessor); this.rules = new ConcurrentHashMap<>(); this.ruleFiles = new ConcurrentHashMap<>(); this.recordedRules = new ConcurrentHashMap<>(); @@ -299,7 +304,8 @@ private void saveRecordedLines() { result.add("# Recorded entries"); result.addAll(recorded); logger.info("Saving {} groupIds to '{}'", result.size(), entry.getValue()); - FileUtils.writeFileWithBackup(entry.getValue(), p -> Files.write(p, result)); + pathProcessor.writeWithBackup( + entry.getValue(), result.stream().collect(Collectors.joining(System.lineSeparator()))); } catch (IOException e) { exceptions.add(e); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java index 40f9ea3b4..fc2c9cd68 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java @@ -38,7 +38,9 @@ import org.eclipse.aether.impl.VersionResolver; import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterManager; import org.eclipse.aether.internal.impl.filter.Filters; -import org.eclipse.aether.internal.test.util.*; +import org.eclipse.aether.internal.test.util.TestFileUtils; +import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; +import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; @@ -61,6 +63,7 @@ import org.eclipse.aether.spi.connector.ArtifactDownload; import org.eclipse.aether.spi.connector.MetadataDownload; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.ArtifactNotFoundException; import org.eclipse.aether.transfer.ArtifactTransferException; import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy; @@ -110,7 +113,7 @@ void setup() { private DefaultArtifactResolver setupArtifactResolver( VersionResolver versionResolver, UpdateCheckManager updateCheckManager) { return new DefaultArtifactResolver( - new TestPathProcessor(), + new PathProcessorSupport(), new StubRepositoryEventDispatcher(), versionResolver, updateCheckManager, diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java index 988d62942..095e554d8 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java @@ -36,7 +36,6 @@ import org.eclipse.aether.deployment.DeployRequest; import org.eclipse.aether.deployment.DeploymentException; import org.eclipse.aether.internal.test.util.TestFileUtils; -import org.eclipse.aether.internal.test.util.TestPathProcessor; import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.metadata.DefaultMetadata; import org.eclipse.aether.metadata.MergeableMetadata; @@ -48,6 +47,7 @@ import org.eclipse.aether.spi.connector.MetadataDownload; import org.eclipse.aether.spi.connector.MetadataUpload; import org.eclipse.aether.spi.connector.RepositoryConnector; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.MetadataNotFoundException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -85,7 +85,7 @@ void setup() throws IOException { connectorProvider = new StubRepositoryConnectorProvider(); deployer = new DefaultDeployer( - new TestPathProcessor(), + new PathProcessorSupport(), new StubRepositoryEventDispatcher(), connectorProvider, new StubRemoteRepositoryManager(), diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java index 59fb22ac3..e6fa5e84f 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java @@ -32,10 +32,13 @@ import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.installation.InstallResult; import org.eclipse.aether.installation.InstallationException; -import org.eclipse.aether.internal.test.util.*; +import org.eclipse.aether.internal.test.util.TestFileUtils; +import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; +import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.metadata.DefaultMetadata; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.metadata.Metadata.Nature; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -84,7 +87,7 @@ void setup() throws IOException { localArtifactFile = new File(session.getLocalRepository().getBasedir(), localArtifactPath); installer = new DefaultInstaller( - new TestPathProcessor(), + new PathProcessorSupport(), new StubRepositoryEventDispatcher(), Collections.emptyMap(), Collections.emptyMap(), diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java index ecc09c821..2646d783c 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java @@ -31,6 +31,7 @@ import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -42,7 +43,8 @@ public class SummaryFileTrustedChecksumsSourceTest extends FileTrustedChecksumsSourceTestSupport { @Override protected FileTrustedChecksumsSourceSupport prepareSubject(RepositorySystemLifecycle lifecycle) { - return new SummaryFileTrustedChecksumsSource(new DefaultLocalPathComposer(), lifecycle); + return new SummaryFileTrustedChecksumsSource( + new DefaultLocalPathComposer(), lifecycle, new PathProcessorSupport()); } @Override diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java index 5f6e0c3fb..4a60622ad 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java @@ -30,6 +30,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.spi.io.PathProcessorSupport; /** * UT for {@link GroupIdRemoteRepositoryFilterSource}. @@ -40,8 +41,8 @@ public class GroupIdRemoteRepositoryFilterSourceTest extends RemoteRepositoryFil @Override protected GroupIdRemoteRepositoryFilterSource getRemoteRepositoryFilterSource( DefaultRepositorySystemSession session, RemoteRepository remoteRepository) { - return groupIdRemoteRepositoryFilterSource = - new GroupIdRemoteRepositoryFilterSource(new DefaultRepositorySystemLifecycle()); + return groupIdRemoteRepositoryFilterSource = new GroupIdRemoteRepositoryFilterSource( + new DefaultRepositorySystemLifecycle(), new PathProcessorSupport()); } @Override diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java index e896eba66..cc41f737f 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java @@ -18,6 +18,7 @@ */ package org.eclipse.aether.spi.io; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -56,7 +57,7 @@ default long lastModified(Path path, long defValue) { * and can be multiple, ranging from "file not found" to cases when FS does not support the setting the mtime. * @since 2.0.0 */ - void setLastModified(Path path, long value) throws IOException; + boolean setLastModified(Path path, long value) throws IOException; /** * Returns size of file, if exists. @@ -94,6 +95,28 @@ default long size(Path path, long defValue) { */ void write(Path target, InputStream source) throws IOException; + /** + * Writes the given data to a file. UTF-8 is assumed as encoding for the data. Creates the necessary directories for + * the target file. In case of an error, the created directories will be left on the file system. + * + * @param target The file to write to, must not be {@code null}. This file will be overwritten. + * @param data The data to write, may be {@code null}. + * @throws IOException If an I/O error occurs. + * @since 2.0.13 + */ + void writeWithBackup(Path target, String data) throws IOException; + + /** + * Writes the given stream to a file. Creates the necessary directories for the target file. In case of an error, + * the created directories will be left on the file system. + * + * @param target The file to write to, must not be {@code null}. This file will be overwritten. + * @param source The stream to write to the file, must not be {@code null}. + * @throws IOException If an I/O error occurs. + * @since 2.0.13 + */ + void writeWithBackup(Path target, InputStream source) throws IOException; + /** * Moves the specified source file to the given target file. If the target file already exists, it is overwritten. * Creates the necessary directories for the target file. In case of an error, the created directories will be left @@ -149,7 +172,68 @@ default void copyWithTimestamp(Path source, Path target) throws IOException { * @see PathProcessor#copy(Path, Path, ProgressListener) */ interface ProgressListener { - void progressed(ByteBuffer buffer) throws IOException; } + + // Temporary files + + /** + * A temporary file, that is removed when closed. + * + * @since 2.0.13 + */ + interface TempFile extends Closeable { + /** + * Returns the path of the created temp file. + */ + Path getPath(); + } + + /** + * A collocated temporary file, that resides next to a "target" file, and is removed when closed. + * + * @since 2.0.13 + */ + interface CollocatedTempFile extends TempFile { + /** + * Upon close, atomically moves temp file to target file it is collocated with overwriting target (if exists). + * Invocation of this method merely signals that caller ultimately wants temp file to replace the target + * file, but when this method returns, the move operation did not yet happen, it will happen when this + * instance is closed. + *

+ * Invoking this method without writing to temp file {@link #getPath()} (thus, not creating a temp + * file to be moved) is considered a bug, a mistake of the caller. Caller of this method should ensure + * that this method is invoked ONLY when the temp file is created and moving it to its final place is + * required. + */ + void move() throws IOException; + } + + /** + * Creates a {@link TempFile} instance and backing temporary file on file system. It will be located in the default + * temporary-file directory. Returned instance should be handled in try-with-resource construct and created + * temp file is removed (if exists) when returned instance is closed. + *

+ * This method uses {@link Files#createTempFile(String, String, java.nio.file.attribute.FileAttribute[])} to create + * the temporary file on file system. + * + * @since 2.0.13 + */ + TempFile newTempFile() throws IOException; + + /** + * Creates a {@link CollocatedTempFile} instance for given file without backing file. The path will be located in + * same directory where given file is, and will reuse its name for generated (randomized) name. Returned instance + * should be handled in try-with-resource and created temp path is removed (if exists) when returned instance is + * closed. The {@link CollocatedTempFile#move()} makes possible to atomically replace passed in file with the + * processed content written into a file backing the {@link CollocatedTempFile} instance. + *

+ * The {@code file} nor it's parent directories have to exist. The parent directories are created if needed. + *

+ * This method uses {@link Path#resolve(String)} to create the temporary file path in passed in file parent + * directory, but it does NOT create backing file on file system. + * + * @since 2.0.13 + */ + CollocatedTempFile newTempFile(Path file) throws IOException; } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessorSupport.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessorSupport.java new file mode 100644 index 000000000..b484ba9bc --- /dev/null +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessorSupport.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.aether.spi.io; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.AccessDeniedException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.util.Objects.requireNonNull; + +/** + * Utility class serving as base of {@link PathProcessor} implementations. This class can be extended or replaced + * (as component) when needed. Also, this class is published in Resolver implementation for path processor interface. + * + * @since 2.0.13 + */ +public class PathProcessorSupport implements PathProcessor { + /** + * Logic borrowed from Commons-Lang3: we really need only this, to decide do we NIO2 file ops or not. + * For some reason non-NIO2 works better on Windows. + */ + protected static final boolean IS_WINDOWS = + System.getProperty("os.name", "unknown").startsWith("Windows"); + + /** + * Escape hatch if atomic move is not desired on system we run on. + */ + protected static final boolean ATOMIC_MOVE = + Boolean.parseBoolean(System.getProperty(PathProcessor.class.getName() + "ATOMIC_MOVE", "true")); + + @Override + public boolean setLastModified(Path path, long value) throws IOException { + try { + Files.setLastModifiedTime(path, FileTime.fromMillis(value)); + return true; + } catch (FileSystemException e) { + // MRESOLVER-536: Java uses generic FileSystemException for some weird cases, + // but some subclasses like AccessDeniedEx should be re-thrown + if (e instanceof AccessDeniedException) { + throw e; + } + return false; + } + } + + @Override + public void write(Path target, String data) throws IOException { + writeFile(target, p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8)), false); + } + + @Override + public void write(Path target, InputStream source) throws IOException { + writeFile(target, p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING), false); + } + + @Override + public void writeWithBackup(Path target, String data) throws IOException { + writeFile(target, p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8)), true); + } + + @Override + public void writeWithBackup(Path target, InputStream source) throws IOException { + writeFile(target, p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING), true); + } + + /** + * A file writer, that accepts a {@link Path} to write some content to. Note: the file denoted by path may exist, + * hence implementation have to ensure it is able to achieve its goal ("replace existing" option or equivalent + * should be used). + */ + @FunctionalInterface + public interface FileWriter { + void write(Path path) throws IOException; + } + + /** + * Utility method to write out file to disk in "atomic" manner, with optional backups (".bak") if needed. This + * ensures that no other thread or process will be able to read not fully written files. Finally, this method + * may create the needed parent directories, if the passed in target parents does not exist. + * + * @param target that is the target file (must be an existing or non-existing file, the path must have parent) + * @param writer the writer that will accept a {@link Path} to write content to + * @param doBackup if {@code true}, and target file is about to be overwritten, a ".bak" file with old contents will + * be created/overwritten + * @throws IOException if at any step IO problem occurs + */ + public void writeFile(Path target, FileWriter writer, boolean doBackup) throws IOException { + requireNonNull(target, "target is null"); + requireNonNull(writer, "writer is null"); + Path parent = requireNonNull(target.getParent(), "target must have parent"); + + try (CollocatedTempFile tempFile = newTempFile(target)) { + writer.write(tempFile.getPath()); + if (doBackup && Files.isRegularFile(target)) { + Files.copy(target, parent.resolve(target.getFileName() + ".bak"), StandardCopyOption.REPLACE_EXISTING); + } + tempFile.move(); + } + } + + @Override + public long copy(Path source, Path target, ProgressListener listener) throws IOException { + try (InputStream in = new BufferedInputStream(Files.newInputStream(source)); + CollocatedTempFile tempTarget = newTempFile(target); + OutputStream out = new BufferedOutputStream(Files.newOutputStream(tempTarget.getPath()))) { + long result = copy(out, in, listener); + tempTarget.move(); + return result; + } + } + + private long copy(OutputStream os, InputStream is, ProgressListener listener) throws IOException { + long total = 0L; + byte[] buffer = new byte[1024 * 32]; + while (true) { + int bytes = is.read(buffer); + if (bytes < 0) { + break; + } + + os.write(buffer, 0, bytes); + + total += bytes; + + if (listener != null && bytes > 0) { + try { + listener.progressed(ByteBuffer.wrap(buffer, 0, bytes)); + } catch (Exception e) { + // too bad + } + } + } + + return total; + } + + @Override + public void move(Path source, Path target) throws IOException { + final StandardCopyOption[] copyOption = ATOMIC_MOVE + ? new StandardCopyOption[] { + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES + } + : new StandardCopyOption[] {StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES}; + if (IS_WINDOWS) { + classicCopy(source, target); + } else { + Files.move(source, target, copyOption); + } + Files.deleteIfExists(source); + } + + // Temp files + + @Override + public TempFile newTempFile() throws IOException { + Path tempFile = Files.createTempFile("resolver", "tmp"); + return new TempFile() { + @Override + public Path getPath() { + return tempFile; + } + + @Override + public void close() throws IOException { + Files.deleteIfExists(tempFile); + } + }; + } + + @Override + public CollocatedTempFile newTempFile(Path file) throws IOException { + Path parent = requireNonNull(file.getParent(), "file must have parent"); + Files.createDirectories(parent); + Path tempFile = parent.resolve(file.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + return new CollocatedTempFile() { + private final AtomicBoolean wantsMove = new AtomicBoolean(false); + private final StandardCopyOption[] copyOption = ATOMIC_MOVE + ? new StandardCopyOption[] {StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING} + : new StandardCopyOption[] {StandardCopyOption.REPLACE_EXISTING}; + + @Override + public Path getPath() { + return tempFile; + } + + @Override + public void move() { + wantsMove.set(true); + } + + @Override + public void close() throws IOException { + if (wantsMove.get()) { + if (IS_WINDOWS) { + classicCopy(tempFile, file); + } else { + Files.move(tempFile, file, copyOption); + } + } + Files.deleteIfExists(tempFile); + } + }; + } + + /** + * On Windows we use pre-NIO2 way to copy files, as for some reason it works. Beat me why. + */ + protected void classicCopy(Path source, Path target) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024 * 32); + byte[] array = buffer.array(); + try (InputStream is = Files.newInputStream(source); + OutputStream os = Files.newOutputStream(target)) { + while (true) { + int bytes = is.read(array); + if (bytes < 0) { + break; + } + os.write(array, 0, bytes); + } + } + } +} diff --git a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index 9b0acb4a1..13219cefe 100644 --- a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -519,7 +519,7 @@ protected Map createRemoteRepositoryFilter HashMap result = new HashMap<>(); result.put( GroupIdRemoteRepositoryFilterSource.NAME, - new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle())); + new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle(), getPathProcessor())); result.put( PrefixesRemoteRepositoryFilterSource.NAME, new PrefixesRemoteRepositoryFilterSource( @@ -586,7 +586,8 @@ protected Map createTrustedChecksumsSources() { new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); result.put( SummaryFileTrustedChecksumsSource.NAME, - new SummaryFileTrustedChecksumsSource(getLocalPathComposer(), getRepositorySystemLifecycle())); + new SummaryFileTrustedChecksumsSource( + getLocalPathComposer(), getRepositorySystemLifecycle(), getPathProcessor())); return result; } @@ -687,6 +688,7 @@ protected BasicRepositoryConnectorFactory createBasicRepositoryConnectorFactory( getTransporterProvider(), getRepositoryLayoutProvider(), getChecksumPolicyProvider(), + getPathProcessor(), getChecksumProcessor(), getProvidedChecksumsSources()); } diff --git a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index 94f61ae8a..e10661488 100644 --- a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -523,7 +523,7 @@ protected Map createRemoteRepositoryFilter HashMap result = new HashMap<>(); result.put( GroupIdRemoteRepositoryFilterSource.NAME, - new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle())); + new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle(), getPathProcessor())); result.put( PrefixesRemoteRepositoryFilterSource.NAME, new PrefixesRemoteRepositoryFilterSource( @@ -590,7 +590,8 @@ protected Map createTrustedChecksumsSources() { new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); result.put( SummaryFileTrustedChecksumsSource.NAME, - new SummaryFileTrustedChecksumsSource(getLocalPathComposer(), getRepositorySystemLifecycle())); + new SummaryFileTrustedChecksumsSource( + getLocalPathComposer(), getRepositorySystemLifecycle(), getPathProcessor())); return result; } @@ -691,6 +692,7 @@ protected BasicRepositoryConnectorFactory createBasicRepositoryConnectorFactory( getTransporterProvider(), getRepositoryLayoutProvider(), getChecksumPolicyProvider(), + getPathProcessor(), getChecksumProcessor(), getProvidedChecksumsSources()); } diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java index b18eafbd8..e282d2860 100644 --- a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java +++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java @@ -18,46 +18,12 @@ */ package org.eclipse.aether.internal.test.util; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; - -import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.spi.io.PathProcessorSupport; /** * A simple file processor implementation to help satisfy component requirements during tests. + * + * @deprecated This class is deprecated and is unused in Resolver. Use {@link PathProcessorSupport} instead. */ -public class TestPathProcessor implements PathProcessor { - - private final TestFileProcessor testFileProcessor = new TestFileProcessor(); - - @Override - public void setLastModified(Path path, long value) throws IOException { - Files.setLastModifiedTime(path, FileTime.fromMillis(value)); - } - - public void mkdirs(Path directory) { - if (directory == null) { - return; - } - testFileProcessor.mkdirs(directory.toFile()); - } - - public void write(Path file, String data) throws IOException { - testFileProcessor.write(file.toFile(), data); - } - - public void write(Path target, InputStream source) throws IOException { - testFileProcessor.write(target.toFile(), source); - } - - public long copy(Path source, Path target, ProgressListener listener) throws IOException { - return testFileProcessor.copy(source.toFile(), target.toFile(), null); - } - - public void move(Path source, Path target) throws IOException { - testFileProcessor.move(source.toFile(), target.toFile()); - } -} +@Deprecated +public class TestPathProcessor extends PathProcessorSupport {} diff --git a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java index 77776a435..3084c2ad7 100644 --- a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java +++ b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java @@ -100,7 +100,6 @@ import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -683,7 +682,7 @@ public void handle(CloseableHttpResponse response) throws IOException, TransferC extractChecksums(response); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) { task.setDataPath(tempFile.getPath(), resume); if (resume && Files.isRegularFile(dataFile)) { try (InputStream inputStream = Files.newInputStream(dataFile)) { diff --git a/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java b/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java index bc7980c52..027d605cd 100644 --- a/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java +++ b/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java @@ -27,11 +27,11 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.DefaultRepositoryCache; import org.eclipse.aether.internal.test.util.TestFileUtils; -import org.eclipse.aether.internal.test.util.TestPathProcessor; import org.eclipse.aether.internal.test.util.http.HttpTransporterTest; import org.eclipse.aether.internal.test.util.http.RecordingTransportListener; import org.eclipse.aether.spi.connector.transport.GetTask; import org.eclipse.aether.spi.connector.transport.PutTask; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -44,7 +44,7 @@ class ApacheTransporterTest extends HttpTransporterTest { public ApacheTransporterTest() { - super(() -> new ApacheTransporterFactory(standardChecksumExtractor(), new TestPathProcessor())); + super(() -> new ApacheTransporterFactory(standardChecksumExtractor(), new PathProcessorSupport())); } @Override diff --git a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java index c55ff5557..7029762e8 100644 --- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java +++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java @@ -76,7 +76,6 @@ import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -335,7 +334,7 @@ protected void implGet(GetTask task) throws Exception { utilGet(task, is, true, length, downloadResumed); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) { task.setDataPath(tempFile.getPath(), downloadResumed); if (downloadResumed && Files.isRegularFile(dataFile)) { try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(dataFile))) { @@ -395,7 +394,7 @@ protected void implPut(PutTask task) throws Exception { request = request.expectContinue(expectContinue); } headers.forEach(request::setHeader); - try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { + try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) { utilPut(task, Files.newOutputStream(tempFile.getPath()), true); request.PUT(HttpRequest.BodyPublishers.ofFile(tempFile.getPath())); diff --git a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java index c7c300a55..a4fc66885 100644 --- a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java +++ b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java @@ -55,7 +55,6 @@ import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.api.Authentication; @@ -315,7 +314,7 @@ protected void implGet(GetTask task) throws Exception { utilGet(task, is, true, length, downloadResumed); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) { task.setDataPath(tempFile.getPath(), downloadResumed); if (downloadResumed && Files.isRegularFile(dataFile)) { try (InputStream inputStream = Files.newInputStream(dataFile)) { diff --git a/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java b/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java index 67b3c976f..ec9af47c2 100644 --- a/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java +++ b/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java @@ -18,8 +18,8 @@ */ package org.eclipse.aether.transport.jetty; -import org.eclipse.aether.internal.test.util.TestPathProcessor; import org.eclipse.aether.internal.test.util.http.HttpTransporterTest; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -59,6 +59,6 @@ protected void testRetryHandler_explicitCount_positive() {} protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() {} public JettyTransporterTest() { - super(() -> new JettyTransporterFactory(standardChecksumExtractor(), new TestPathProcessor())); + super(() -> new JettyTransporterFactory(standardChecksumExtractor(), new PathProcessorSupport())); } } diff --git a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java index 3299ec1ed..8566e07f9 100644 --- a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java +++ b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java @@ -44,9 +44,11 @@ import org.eclipse.aether.spi.connector.transport.PeekTask; import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; + +import static java.util.Objects.requireNonNull; /** * A transporter for S3 backed by MinIO Java. @@ -62,10 +64,13 @@ final class MinioTransporter extends AbstractTransporter implements Transporter private final ObjectNameMapper objectNameMapper; + private final PathProcessor pathProcessor; + MinioTransporter( RepositorySystemSession session, RemoteRepository repository, - ObjectNameMapperFactory objectNameMapperFactory) + ObjectNameMapperFactory objectNameMapperFactory, + PathProcessor pathProcessor) throws NoTransporterException { try { URI uri = new URI(repository.getUrl()).parseServerAuthority(); @@ -121,6 +126,7 @@ final class MinioTransporter extends AbstractTransporter implements Transporter .credentialsProvider(credentialsProvider) .build(); this.objectNameMapper = objectNameMapperFactory.create(session, repository, client, headers); + this.pathProcessor = requireNonNull(pathProcessor); } @Override @@ -158,7 +164,7 @@ protected void implGet(GetTask task) throws Exception { if (dataFile == null) { utilGet(task, stream, true, -1, false); } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) { task.setDataPath(tempFile.getPath(), false); utilGet(task, stream, true, -1, false); tempFile.move(); @@ -176,7 +182,7 @@ protected void implPut(PutTask task) throws Exception { task.getListener().transportStarted(0, task.getDataLength()); final Path dataFile = task.getDataPath(); if (dataFile == null) { - try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { + try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) { Files.copy(task.newInputStream(), tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING); client.uploadObject(UploadObjectArgs.builder() .bucket(objectName.getBucket()) diff --git a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporterFactory.java b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporterFactory.java index c5240f533..1f1851772 100644 --- a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporterFactory.java +++ b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporterFactory.java @@ -27,6 +27,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.transport.Transporter; import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.util.ConfigUtils; @@ -47,9 +48,13 @@ public final class MinioTransporterFactory implements TransporterFactory { private final Map objectNameMapperFactories; + private final PathProcessor pathProcessor; + @Inject - public MinioTransporterFactory(Map objectNameMapperFactories) { + public MinioTransporterFactory( + Map objectNameMapperFactories, PathProcessor pathProcessor) { this.objectNameMapperFactories = requireNonNull(objectNameMapperFactories, "objectNameMapperFactories"); + this.pathProcessor = requireNonNull(pathProcessor, "pathProcessor"); } @Override @@ -96,6 +101,6 @@ public Transporter newInstance(RepositorySystemSession session, RemoteRepository throw new IllegalArgumentException("Unknown object name mapper configured '" + objectNameMapperConf + "' for repository " + repository.getId()); } - return new MinioTransporter(session, adjusted, objectNameMapperFactory); + return new MinioTransporter(session, adjusted, objectNameMapperFactory, pathProcessor); } } diff --git a/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterFactoryTest.java b/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterFactoryTest.java index 7ce8d2354..b2c3c83d0 100644 --- a/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterFactoryTest.java +++ b/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterFactoryTest.java @@ -28,6 +28,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transport.minio.internal.FixedBucketObjectNameMapperFactory; import org.eclipse.aether.transport.minio.internal.RepositoryIdObjectNameMapperFactory; @@ -55,7 +56,7 @@ void startSuite() throws Exception { factories.put(RepositoryIdObjectNameMapperFactory.NAME, new RepositoryIdObjectNameMapperFactory()); factories.put(FixedBucketObjectNameMapperFactory.NAME, new FixedBucketObjectNameMapperFactory()); - factory = new MinioTransporterFactory(factories); + factory = new MinioTransporterFactory(factories, new PathProcessorSupport()); session = new DefaultRepositorySystemSession(h -> true); repository = new RemoteRepository.Builder("repo", "default", "minio+http://localhost") .setAuthentication(new AuthenticationBuilder() diff --git a/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterIT.java b/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterIT.java index 1547bd639..4760e6ae8 100644 --- a/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterIT.java +++ b/maven-resolver-transport-minio/src/test/java/org/eclipse/aether/transport/minio/MinioTransporterIT.java @@ -34,9 +34,10 @@ import org.eclipse.aether.spi.connector.transport.PeekTask; import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transport.minio.internal.RepositoryIdObjectNameMapperFactory; -import org.eclipse.aether.util.FileUtils; import org.eclipse.aether.util.repository.AuthenticationBuilder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -58,6 +59,7 @@ class MinioTransporterIT { private static final String OBJECT_NAME = "dir/file.txt"; private static final String OBJECT_CONTENT = "content"; + private PathProcessor pathProcessor; private MinIOContainer minioContainer; private RepositorySystemSession session; private ObjectNameMapperFactory objectNameMapperFactory; @@ -66,6 +68,7 @@ class MinioTransporterIT { void startSuite() throws Exception { Files.createDirectories(Paths.get(System.getProperty("java.io.tmpdir"))); // hack for Surefire + pathProcessor = new PathProcessorSupport(); minioContainer = new MinIOContainer("minio/minio:latest"); minioContainer.start(); try (MinioClient minioClient = MinioClient.builder() @@ -73,7 +76,7 @@ void startSuite() throws Exception { .credentials(minioContainer.getUserName(), minioContainer.getPassword()) .build()) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build()); - try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { + try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) { Files.write(tempFile.getPath(), OBJECT_CONTENT.getBytes(StandardCharsets.UTF_8)); minioClient.uploadObject(UploadObjectArgs.builder() .bucket(BUCKET_NAME) @@ -118,7 +121,7 @@ protected RemoteRepository newRepo(RepositoryAuth auth) { @Test void peekWithoutAuth() throws NoTransporterException { try { - new MinioTransporter(session, newRepo(RepositoryAuth.WITHOUT), objectNameMapperFactory); + new MinioTransporter(session, newRepo(RepositoryAuth.WITHOUT), objectNameMapperFactory, pathProcessor); fail("Should throw"); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("No accessKey and/or secretKey provided")); @@ -128,7 +131,7 @@ void peekWithoutAuth() throws NoTransporterException { @Test void peekWithWrongAuth() throws NoTransporterException { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WRONG), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WRONG), objectNameMapperFactory, pathProcessor)) { try { transporter.peek(new PeekTask(URI.create("test"))); fail("Should throw"); @@ -142,7 +145,7 @@ void peekWithWrongAuth() throws NoTransporterException { @Test void peekNonexistent() throws NoTransporterException { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { try { transporter.peek(new PeekTask(URI.create("test"))); fail("Should throw"); @@ -156,7 +159,7 @@ void peekNonexistent() throws NoTransporterException { @Test void peekExistent() throws Exception { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { transporter.peek(new PeekTask(URI.create(OBJECT_NAME))); // Should not throw } @@ -165,7 +168,7 @@ void peekExistent() throws Exception { @Test void getNonexistent() throws NoTransporterException { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { try { transporter.get(new GetTask(URI.create("test"))); fail("Should throw"); @@ -179,7 +182,7 @@ void getNonexistent() throws NoTransporterException { @Test void getExistent() throws Exception { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { GetTask task = new GetTask(URI.create(OBJECT_NAME)); transporter.get(task); assertEquals(OBJECT_CONTENT, new String(task.getDataBytes(), StandardCharsets.UTF_8)); @@ -189,7 +192,7 @@ void getExistent() throws Exception { @Test void putNonexistent() throws Exception { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { URI uri = URI.create("test"); transporter.put(new PutTask(uri).setDataBytes(OBJECT_CONTENT.getBytes(StandardCharsets.UTF_8))); GetTask task = new GetTask(uri); @@ -201,7 +204,7 @@ void putNonexistent() throws Exception { @Test void putExistent() throws Exception { try (MinioTransporter transporter = - new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory)) { + new MinioTransporter(session, newRepo(RepositoryAuth.WITH), objectNameMapperFactory, pathProcessor)) { URI uri = URI.create(OBJECT_NAME); GetTask task = new GetTask(uri); transporter.get(task); diff --git a/maven-resolver-transport-wagon/pom.xml b/maven-resolver-transport-wagon/pom.xml index 3b5b4133c..d8c55463e 100644 --- a/maven-resolver-transport-wagon/pom.xml +++ b/maven-resolver-transport-wagon/pom.xml @@ -86,6 +86,11 @@ maven-resolver-test-util test + + org.apache.maven.resolver + maven-resolver-impl + test + org.codehaus.plexus plexus-testing diff --git a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java index 201a82b0c..107f2b8d2 100644 --- a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java +++ b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java @@ -53,9 +53,9 @@ import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.TransportTask; import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,6 +75,8 @@ final class WagonTransporter implements Transporter { private final RepositorySystemSession session; + private final PathProcessor pathProcessor; + private final AuthenticationContext repoAuthContext; private final AuthenticationContext proxyAuthContext; @@ -101,12 +103,14 @@ final class WagonTransporter implements Transporter { WagonProvider wagonProvider, WagonConfigurator wagonConfigurator, RemoteRepository repository, - RepositorySystemSession session) + RepositorySystemSession session, + PathProcessor pathProcessor) throws NoTransporterException { this.wagonProvider = wagonProvider; this.wagonConfigurator = wagonConfigurator; this.repository = repository; this.session = session; + this.pathProcessor = pathProcessor; wagonRepo = new Repository(repository.getId(), repository.getUrl()); wagonRepo.setPermissions(getPermissions(repository.getId(), session)); @@ -340,12 +344,12 @@ public void peek(PeekTask task) throws Exception { @Override public void get(GetTask task) throws Exception { - execute(task, new GetTaskRunner(task)); + execute(task, new GetTaskRunner(pathProcessor, task)); } @Override public void put(PutTask task) throws Exception { - execute(task, new PutTaskRunner(task)); + execute(task, new PutTaskRunner(pathProcessor, task)); } private void execute(TransportTask task, TaskRunner runner) throws Exception { @@ -407,9 +411,12 @@ public void run(Wagon wagon) throws WagonException { private static class GetTaskRunner implements TaskRunner { + private final PathProcessor pathProcessor; + private final GetTask task; - GetTaskRunner(GetTask task) { + GetTaskRunner(PathProcessor pathProcessor, GetTask task) { + this.pathProcessor = pathProcessor; this.task = task; } @@ -423,8 +430,8 @@ public void run(Wagon wagon) throws IOException, WagonException { } } else { // if file == null -> $TMP used, otherwise we place tmp file next to file - try (FileUtils.TempFile tempFile = - file == null ? FileUtils.newTempFile() : FileUtils.newTempFile(file.toPath())) { + try (PathProcessor.TempFile tempFile = + file == null ? pathProcessor.newTempFile() : pathProcessor.newTempFile(file.toPath())) { File dst = tempFile.getPath().toFile(); wagon.get(src, dst); /* @@ -437,7 +444,7 @@ public void run(Wagon wagon) throws IOException, WagonException { } if (file != null) { - ((FileUtils.CollocatedTempFile) tempFile).move(); + ((PathProcessor.CollocatedTempFile) tempFile).move(); } else { try (OutputStream outputStream = task.newOutputStream()) { Files.copy(dst.toPath(), outputStream); @@ -450,9 +457,12 @@ public void run(Wagon wagon) throws IOException, WagonException { private static class PutTaskRunner implements TaskRunner { + private final PathProcessor pathProcessor; + private final PutTask task; - PutTaskRunner(PutTask task) { + PutTaskRunner(PathProcessor pathProcessor, PutTask task) { + this.pathProcessor = pathProcessor; this.task = task; } @@ -466,7 +476,7 @@ public void run(Wagon wagon) throws WagonException, IOException { ((StreamingWagon) wagon).putFromStream(src, dst, task.getDataLength(), -1); } } else if (file == null) { - try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { + try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) { try (InputStream inputStream = task.newInputStream()) { Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING); } diff --git a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporterFactory.java b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporterFactory.java index b6393d6dc..1958e603f 100644 --- a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporterFactory.java +++ b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporterFactory.java @@ -25,6 +25,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.transport.Transporter; import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import static java.util.Objects.requireNonNull; @@ -42,12 +43,16 @@ public final class WagonTransporterFactory implements TransporterFactory { private final WagonConfigurator wagonConfigurator; + private final PathProcessor pathProcessor; + private float priority = -1.0f; @Inject - public WagonTransporterFactory(WagonProvider wagonProvider, WagonConfigurator wagonConfigurator) { + public WagonTransporterFactory( + WagonProvider wagonProvider, WagonConfigurator wagonConfigurator, PathProcessor pathProcessor) { this.wagonProvider = wagonProvider; this.wagonConfigurator = wagonConfigurator; + this.pathProcessor = pathProcessor; } @Override @@ -72,6 +77,6 @@ public Transporter newInstance(RepositorySystemSession session, RemoteRepository requireNonNull(session, "session cannot be null"); requireNonNull(repository, "repository cannot be null"); - return new WagonTransporter(wagonProvider, wagonConfigurator, repository, session); + return new WagonTransporter(wagonProvider, wagonConfigurator, repository, session, pathProcessor); } } diff --git a/maven-resolver-transport-wagon/src/test/java/org/eclipse/aether/transport/wagon/AbstractWagonTransporterTest.java b/maven-resolver-transport-wagon/src/test/java/org/eclipse/aether/transport/wagon/AbstractWagonTransporterTest.java index 9d002c00a..b4c048fbc 100644 --- a/maven-resolver-transport-wagon/src/test/java/org/eclipse/aether/transport/wagon/AbstractWagonTransporterTest.java +++ b/maven-resolver-transport-wagon/src/test/java/org/eclipse/aether/transport/wagon/AbstractWagonTransporterTest.java @@ -39,6 +39,7 @@ import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.Transporter; import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.spi.io.PathProcessorSupport; import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.util.repository.AuthenticationBuilder; @@ -99,7 +100,8 @@ public void release(Wagon wagon) {} public void configure(Wagon wagon, Object configuration) { ((Configurable) wagon).setConfiguration(configuration); } - }); + }, + new PathProcessorSupport()); id = UUID.randomUUID().toString().replace("-", ""); fs = MemWagonUtils.getFilesystem(id); fs.put("file.txt", "test"); diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/FileUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/FileUtils.java index 4e4e14b13..736420762 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/FileUtils.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/FileUtils.java @@ -35,7 +35,9 @@ * A utility class to write files. * * @since 1.9.0 + * @deprecated Do not use this class; is not used in Resolver (see corresponding processor components in {@code org.eclipse.aether.spi.io} package). */ +@Deprecated public final class FileUtils { /** * Logic borrowed from Commons-Lang3: we really need only this, to decide do we NIO2 file ops or not. @@ -55,6 +57,7 @@ public final class FileUtils { private FileUtils() { // hide constructor } + /** * A temporary file, that is removed when closed. */