From d8bd3c6f2f6e65474709f3168b5164b8d9910406 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 13 Feb 2024 23:26:07 +0100 Subject: [PATCH] Paths WIP --- .../org/eclipse/aether/RepositoryEvent.java | 40 ++- .../aether/RepositorySystemSession.java | 56 +++- .../aether/artifact/AbstractArtifact.java | 31 +- .../org/eclipse/aether/artifact/Artifact.java | 23 ++ .../aether/artifact/DefaultArtifact.java | 49 +++- .../aether/metadata/AbstractMetadata.java | 26 +- .../aether/metadata/DefaultMetadata.java | 80 +++++- .../aether/metadata/MergeableMetadata.java | 16 ++ .../org/eclipse/aether/metadata/Metadata.java | 22 ++ .../repository/LocalArtifactResult.java | 36 ++- .../repository/LocalMetadataResult.java | 34 ++- .../aether/repository/LocalRepository.java | 67 ++++- .../NoLocalRepositoryManagerException.java | 2 +- .../aether/repository/RepositoryUriUtils.java | 38 ++- .../aether/repository/WorkspaceReader.java | 13 + .../ArtifactResolutionException.java | 2 +- .../aether/resolution/ArtifactResult.java | 6 +- .../aether/resolution/MetadataResult.java | 4 +- .../transfer/MetadataNotFoundException.java | 2 +- .../aether/transfer/TransferResource.java | 45 ++- .../repository/LocalRepositoryTest.java | 52 ++++ .../aether/transfer/TransferEventTest.java | 3 +- .../basic/BasicRepositoryConnector.java | 56 ++-- .../BasicRepositoryConnectorFactory.java | 10 +- .../connector/basic/ChecksumCalculator.java | 10 +- .../connector/basic/ChecksumValidator.java | 40 +-- .../basic/ChecksumCalculatorTest.java | 2 +- .../basic/ChecksumValidatorTest.java | 26 +- .../maven-resolver-demo-snippets/pom.xml | 4 + .../resolver/examples/DeployArtifacts.java | 4 +- .../resolver/examples/InstallArtifacts.java | 4 +- .../resolver/examples/ResolveArtifact.java | 15 +- .../ResolveTransitiveDependencies.java | 6 +- .../examples/ReverseDependencyTree.java | 15 +- .../SupplierRepositorySystemFactory.java | 4 +- .../maven/resolver/examples/util/Booter.java | 17 +- .../org/eclipse/aether/impl/UpdateCheck.java | 32 ++- .../impl/DefaultArtifactResolver.java | 75 ++--- .../impl/DefaultChecksumProcessor.java | 88 ++++++ .../aether/internal/impl/DefaultDeployer.java | 68 +++-- .../internal/impl/DefaultFileProcessor.java | 3 + .../internal/impl/DefaultInstaller.java | 57 ++-- .../impl/DefaultLocalRepositoryProvider.java | 4 +- .../impl/DefaultMetadataResolver.java | 87 +++--- .../internal/impl/DefaultPathProcessor.java | 95 ++++++ .../impl/DefaultTrackingFileManager.java | 106 ++++--- .../impl/DefaultUpdateCheckManager.java | 99 ++++--- .../impl/EnhancedLocalRepositoryManager.java | 55 ++-- ...EnhancedLocalRepositoryManagerFactory.java | 2 +- .../impl/SimpleLocalRepositoryManager.java | 29 +- .../SimpleLocalRepositoryManagerFactory.java | 2 +- .../internal/impl/TrackingFileManager.java | 18 ++ ...SparseDirectoryTrustedChecksumsSource.java | 13 +- ...hecksumsArtifactResolverPostProcessor.java | 10 +- .../impl/session/DefaultCloseableSession.java | 22 +- .../impl/session/DefaultSessionBuilder.java | 69 ++++- .../synccontext/named/BasedirNameMapper.java | 31 +- .../named/DiscriminatingNameMapper.java | 4 +- .../impl/synccontext/named/GAVNameMapper.java | 2 +- .../impl/synccontext/named/NameMapper.java | 5 + .../impl/DefaultArtifactResolverTest.java | 12 +- .../DefaultChecksumPolicyProviderTest.java | 4 +- .../internal/impl/DefaultDeployerTest.java | 17 +- .../internal/impl/DefaultInstallerTest.java | 12 +- .../impl/DefaultMetadataResolverTest.java | 6 +- .../impl/DefaultUpdateCheckManagerTest.java | 5 +- .../EnhancedLocalRepositoryManagerTest.java | 2 +- ...hancedSplitLocalRepositoryManagerTest.java | 2 +- .../internal/impl/FailChecksumPolicyTest.java | 4 +- .../impl/RecordingRepositoryConnector.java | 9 +- .../SimpleLocalRepositoryManagerTest.java | 2 +- .../internal/impl/WarnChecksumPolicyTest.java | 4 +- ...seDirectoryTrustedChecksumsSourceTest.java | 6 +- .../named/BasedirNameMapperTest.java | 24 +- .../named/NameMapperTestSupport.java | 7 +- .../providers/FileLockNamedLockFactory.java | 8 +- .../FileLockNamedLockFactorySupportTest.java | 5 +- .../spi/connector/ArtifactDownload.java | 29 +- .../spi/connector/ArtifactTransfer.java | 34 ++- .../aether/spi/connector/ArtifactUpload.java | 24 +- .../spi/connector/MetadataDownload.java | 27 +- .../spi/connector/MetadataTransfer.java | 34 ++- .../aether/spi/connector/MetadataUpload.java | 24 +- .../checksum/ChecksumAlgorithmHelper.java | 18 +- .../spi/connector/transport/GetTask.java | 75 ++++- .../spi/connector/transport/PutTask.java | 52 +++- .../aether/spi/io/ChecksumProcessor.java | 45 +++ .../eclipse/aether/spi/io/FileProcessor.java | 5 +- .../eclipse/aether/spi/io/PathProcessor.java | 127 ++++++++ .../supplier/RepositorySystemSupplier.java | 46 ++- .../RepositorySystemSupplierTest.java | 12 +- .../test/util/http/HttpTransporterTest.java | 24 +- .../test/util/TestChecksumProcessor.java | 41 +++ .../internal/test/util/TestPathProcessor.java | 59 ++++ .../transport/apache/ApacheTransporter.java | 27 +- maven-resolver-transport-file/pom.xml | 10 + .../transport/file/FileTransporter.java | 44 ++- .../transport/file/FileTransporterTest.java | 271 ++++++++++++------ .../aether/transport/jdk/JdkTransporter.java | 32 ++- .../transport/jdk/JdkTransporterFactory.java | 8 +- .../transport/jdk/JdkTransporterTest.java | 5 +- .../transport/jdk/JdkTransporterFactory.java | 3 +- .../transport/jetty/JettyTransporter.java | 24 +- .../jetty/PutTaskRequestContent.java | 4 +- .../eclipse/aether/util/DirectoryUtils.java | 9 +- .../org/eclipse/aether/util/FileUtils.java | 2 +- .../util/artifact/DelegatingArtifact.java | 27 ++ .../aether/util/artifact/SubArtifact.java | 76 ++++- .../util/graph/visitor/NodeListGenerator.java | 30 +- .../ChainedLocalRepositoryManager.java | 11 +- .../aether/util/artifact/SubArtifactTest.java | 2 +- pom.xml | 8 +- 112 files changed, 2441 insertions(+), 793 deletions(-) rename maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/PathUtils.java => maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryUriUtils.java (79%) create mode 100644 maven-resolver-api/src/test/java/org/eclipse/aether/repository/LocalRepositoryTest.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultChecksumProcessor.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java create mode 100644 maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/ChecksumProcessor.java create mode 100644 maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java create mode 100644 maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumProcessor.java create mode 100644 maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositoryEvent.java b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositoryEvent.java index a75bfa5e1..a1c00dcb3 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositoryEvent.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositoryEvent.java @@ -19,6 +19,7 @@ package org.eclipse.aether; import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -149,7 +150,7 @@ public enum EventType { private final ArtifactRepository repository; - private final File file; + private final Path path; private final List exceptions; @@ -161,7 +162,7 @@ public enum EventType { artifact = builder.artifact; metadata = builder.metadata; repository = builder.repository; - file = builder.file; + path = builder.path; exceptions = builder.exceptions; trace = builder.trace; } @@ -206,9 +207,21 @@ public Metadata getMetadata() { * Gets the file involved in the event (if any). * * @return The involved file or {@code null} if none. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the file involved in the event (if any). + * + * @return The involved file or {@code null} if none. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -260,8 +273,8 @@ public String toString() { if (getMetadata() != null) { buffer.append(" ").append(getMetadata()); } - if (getFile() != null) { - buffer.append(" (").append(getFile()).append(")"); + if (getPath() != null) { + buffer.append(" (").append(getPath()).append(")"); } if (getRepository() != null) { buffer.append(" @ ").append(getRepository()); @@ -284,7 +297,7 @@ public static final class Builder { ArtifactRepository repository; - File file; + Path path; List exceptions = Collections.emptyList(); @@ -339,9 +352,22 @@ public Builder setRepository(ArtifactRepository repository) { * * @param file The involved file, may be {@code null}. * @return This event builder for chaining, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public Builder setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the file involved in the event. + * + * @param path The involved file, may be {@code null}. + * @return This event builder for chaining, never {@code null}. + * @since 2.0.0 + */ + public Builder setPath(Path path) { + this.path = path; return this; } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java index e85780a4e..d2abbda3e 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java @@ -19,8 +19,8 @@ package org.eclipse.aether; import java.io.Closeable; -import java.io.File; -import java.util.List; +import java.nio.file.Path; +import java.util.Collection; import java.util.Map; import java.util.function.Supplier; @@ -397,6 +397,14 @@ interface SessionBuilder { */ SessionBuilder setSystemScopeHandler(SystemScopeHandler systemScopeHandler); + /** + * Adds on session ended handler to be immediately registered when this builder creates session. + * + * @param handler The on session ended handler, may not be {@code null}. + * @return The session for chaining, never {@code null}. + */ + SessionBuilder addOnSessionEndedHandler(Runnable handler); + /** * Sets the custom session data supplier associated with this session. * Note: The supplier will be used for every built session out of this builder instance, so if supplier supplies @@ -419,25 +427,25 @@ interface SessionBuilder { /** * Shortcut method to set up local repository manager directly onto builder. There must be at least one non-null - * {@link File} passed in this method. In case multiple files, session builder will use chained local repository + * {@link Path} passed in this method. In case multiple files, session builder will use chained local repository * manager. * * @param baseDirectories The local repository base directories. * @return This session for chaining, never {@code null}. * @see #withLocalRepositories(LocalRepository...) */ - SessionBuilder withLocalRepositoryBaseDirectories(File... baseDirectories); + SessionBuilder withLocalRepositoryBaseDirectories(Path... baseDirectories); /** * Shortcut method to set up local repository manager directly onto builder. There must be at least one non-null - * {@link File} present in passed in list. In case multiple files, session builder will use chained local + * {@link Path} present in passed in list. In case multiple files, session builder will use chained local * repository manager. * * @param baseDirectories The local repository base directories. * @return This session for chaining, never {@code null}. - * @see #withLocalRepositories(List) + * @see #withLocalRepositories(Collection) */ - SessionBuilder withLocalRepositoryBaseDirectories(List baseDirectories); + SessionBuilder withLocalRepositoryBaseDirectories(Collection baseDirectories); /** * Shortcut method to set up local repository manager directly onto builder. There must be at least one non-null @@ -457,7 +465,39 @@ interface SessionBuilder { * @param localRepositories The local repositories. * @return This session for chaining, never {@code null}. */ - SessionBuilder withLocalRepositories(List localRepositories); + SessionBuilder withLocalRepositories(Collection localRepositories); + + /** + * Adds the listeners to be notified of actions in the repository system. + * + * @param repositoryListeners The repository listeners, never {@code null}. + * @return This session for chaining, never {@code null}. + */ + SessionBuilder withRepositoryListener(RepositoryListener... repositoryListeners); + + /** + * Adds the listeners to be notified of actions in the repository system. + * + * @param repositoryListeners The repository listeners, never {@code null}. + * @return This session for chaining, never {@code null}. + */ + SessionBuilder withRepositoryListener(Collection repositoryListeners); + + /** + * Adds the listener to be notified of uploads/downloads by the repository system. + * + * @param transferListeners The transfer listeners, never {@code null}. + * @return This session for chaining, never {@code null}. + */ + SessionBuilder withTransferListener(TransferListener... transferListeners); + + /** + * Adds the listener to be notified of uploads/downloads by the repository system. + * + * @param transferListeners The transfer listeners, never {@code null}. + * @return This session for chaining, never {@code null}. + */ + SessionBuilder withTransferListener(Collection transferListeners); /** * Shortcut method to shallow-copy passed in session into current builder. diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/AbstractArtifact.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/AbstractArtifact.java index 92e59f23e..b204069f4 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/AbstractArtifact.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/AbstractArtifact.java @@ -19,6 +19,7 @@ package org.eclipse.aether.artifact; import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -35,6 +36,7 @@ public abstract class AbstractArtifact implements Artifact { private static final Pattern SNAPSHOT_TIMESTAMP = Pattern.compile("^(.*-)?([0-9]{8}\\.[0-9]{6}-[0-9]+)$"); + @Override public boolean isSnapshot() { return isSnapshot(getVersion()); } @@ -43,6 +45,7 @@ private static boolean isSnapshot(String version) { return version.endsWith(SNAPSHOT) || SNAPSHOT_TIMESTAMP.matcher(version).matches(); } + @Override public String getBaseVersion() { return toBaseVersion(getVersion()); } @@ -76,28 +79,36 @@ private static String toBaseVersion(String version) { * @param version The version of the artifact, may be {@code null}. * @param properties The properties of the artifact, may be {@code null} if none. The method may assume immutability * of the supplied map, i.e. need not copy it. - * @param file The resolved file of the artifact, may be {@code null}. + * @param path The resolved file of the artifact, may be {@code null}. * @return The new artifact instance, never {@code null}. */ - private Artifact newInstance(String version, Map properties, File file) { + private Artifact newInstance(String version, Map properties, Path path) { return new DefaultArtifact( - getGroupId(), getArtifactId(), getClassifier(), getExtension(), version, file, properties); + getGroupId(), getArtifactId(), getClassifier(), getExtension(), version, path, properties); } + @Override public Artifact setVersion(String version) { String current = getVersion(); if (current.equals(version) || (version == null && current.isEmpty())) { return this; } - return newInstance(version, getProperties(), getFile()); + return newInstance(version, getProperties(), getPath()); } + @Deprecated + @Override public Artifact setFile(File file) { - File current = getFile(); - if (Objects.equals(current, file)) { + return setPath(file != null ? file.toPath() : null); + } + + @Override + public Artifact setPath(Path path) { + Path current = getPath(); + if (Objects.equals(current, path)) { return this; } - return newInstance(getVersion(), getProperties(), file); + return newInstance(getVersion(), getProperties(), path); } public Artifact setProperties(Map properties) { @@ -105,7 +116,7 @@ public Artifact setProperties(Map properties) { if (current.equals(properties) || (properties == null && current.isEmpty())) { return this; } - return newInstance(getVersion(), copyProperties(properties), getFile()); + return newInstance(getVersion(), copyProperties(properties), getPath()); } public String getProperty(String key, String defaultValue) { @@ -163,7 +174,7 @@ public boolean equals(Object obj) { && Objects.equals(getVersion(), that.getVersion()) && Objects.equals(getExtension(), that.getExtension()) && Objects.equals(getClassifier(), that.getClassifier()) - && Objects.equals(getFile(), that.getFile()) + && Objects.equals(getPath(), that.getPath()) && Objects.equals(getProperties(), that.getProperties()); } @@ -180,7 +191,7 @@ public int hashCode() { hash = hash * 31 + getExtension().hashCode(); hash = hash * 31 + getClassifier().hashCode(); hash = hash * 31 + getVersion().hashCode(); - hash = hash * 31 + hash(getFile()); + hash = hash * 31 + hash(getPath()); return hash; } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/Artifact.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/Artifact.java index d9736c126..7f920113b 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/Artifact.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/Artifact.java @@ -19,6 +19,7 @@ package org.eclipse.aether.artifact; import java.io.File; +import java.nio.file.Path; import java.util.Map; /** @@ -97,17 +98,39 @@ public interface Artifact { * callers must not assume any relationship between an artifact's filename and its coordinates. * * @return The file or {@code null} if the artifact isn't resolved. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated File getFile(); + /** + * Gets the file of this artifact. Note that only resolved artifacts have a file associated with them. In general, + * callers must not assume any relationship between an artifact's filename and its coordinates. + * + * @return The file or {@code null} if the artifact isn't resolved. + * @since 2.0.0 + */ + Path getPath(); + /** * Sets the file of the artifact. * * @param file The file of the artifact, may be {@code null} * @return The new artifact, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated Artifact setFile(File file); + /** + * Sets the file of the artifact. + * + * @param path The file of the artifact, may be {@code null} + * @return The new artifact, never {@code null}. + * @since 2.0.0 + */ + Artifact setPath(Path path); + /** * Gets the specified property. * diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java index 4e5b21053..9050da712 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java @@ -19,6 +19,7 @@ package org.eclipse.aether.artifact; import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -43,7 +44,7 @@ public final class DefaultArtifact extends AbstractArtifact { private final String extension; - private final File file; + private final Path path; private final Map properties; @@ -116,7 +117,7 @@ public DefaultArtifact(String coords, Map properties, ArtifactTy extension = get(m.group(4), type == null ? "jar" : type.getExtension()); classifier = get(m.group(6), type == null ? "" : type.getClassifier()); this.version = emptify(m.group(7)); - this.file = null; + this.path = null; this.properties = merge(properties, (type != null) ? type.getProperties() : null); } @@ -205,7 +206,7 @@ public DefaultArtifact( this.extension = emptify(type.getExtension()); } this.version = emptify(version); - this.file = null; + this.path = null; this.properties = merge(properties, (type != null) ? type.getProperties() : null); } @@ -253,7 +254,36 @@ public DefaultArtifact( this.classifier = emptify(classifier); this.extension = emptify(extension); this.version = emptify(version); - this.file = file; + this.path = file != null ? file.toPath() : null; + this.properties = copyProperties(properties); + } + + /** + * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the + * coordinates is equivalent to specifying an empty string. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param classifier The classifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + * @param properties The properties of the artifact, may be {@code null} if none. + * @param path The resolved file of the artifact, may be {@code null}. + */ + public DefaultArtifact( + String groupId, + String artifactId, + String classifier, + String extension, + String version, + Map properties, + Path path) { + this.groupId = emptify(groupId); + this.artifactId = emptify(artifactId); + this.classifier = emptify(classifier); + this.extension = emptify(extension); + this.version = emptify(version); + this.path = path; this.properties = copyProperties(properties); } @@ -263,7 +293,7 @@ public DefaultArtifact( String classifier, String extension, String version, - File file, + Path path, Map properties) { // NOTE: This constructor assumes immutability of the provided properties, for internal use only this.groupId = emptify(groupId); @@ -271,7 +301,7 @@ public DefaultArtifact( this.classifier = emptify(classifier); this.extension = emptify(extension); this.version = emptify(version); - this.file = file; + this.path = path; this.properties = properties; } @@ -299,8 +329,13 @@ public String getExtension() { return extension; } + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + public Path getPath() { + return path; } public Map getProperties() { diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java index 486f0cfd4..8d83e091e 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/AbstractMetadata.java @@ -19,6 +19,7 @@ package org.eclipse.aether.metadata; import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -29,27 +30,40 @@ */ public abstract class AbstractMetadata implements Metadata { - private Metadata newInstance(Map properties, File file) { + private Metadata newInstance(Map properties, Path path) { return new DefaultMetadata( - getGroupId(), getArtifactId(), getVersion(), getType(), getNature(), file, properties); + getGroupId(), getArtifactId(), getVersion(), getType(), getNature(), path, properties); } + @Deprecated + @Override public Metadata setFile(File file) { File current = getFile(); if (Objects.equals(current, file)) { return this; } - return newInstance(getProperties(), file); + return newInstance(getProperties(), file != null ? file.toPath() : null); + } + + @Override + public Metadata setPath(Path path) { + Path current = getPath(); + if (Objects.equals(current, path)) { + return this; + } + return newInstance(getProperties(), path); } + @Override public Metadata setProperties(Map properties) { Map current = getProperties(); if (current.equals(properties) || (properties == null && current.isEmpty())) { return this; } - return newInstance(copyProperties(properties), getFile()); + return newInstance(copyProperties(properties), getPath()); } + @Override public String getProperty(String key, String defaultValue) { String value = getProperties().get(key); return (value != null) ? value : defaultValue; @@ -108,7 +122,7 @@ public boolean equals(Object obj) { && Objects.equals(getVersion(), that.getVersion()) && Objects.equals(getType(), that.getType()) && Objects.equals(getNature(), that.getNature()) - && Objects.equals(getFile(), that.getFile()) + && Objects.equals(getPath(), that.getPath()) && Objects.equals(getProperties(), that.getProperties()); } @@ -125,7 +139,7 @@ public int hashCode() { hash = hash * 31 + getType().hashCode(); hash = hash * 31 + getNature().hashCode(); hash = hash * 31 + getVersion().hashCode(); - hash = hash * 31 + hash(getFile()); + hash = hash * 31 + hash(getPath()); return hash; } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/DefaultMetadata.java b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/DefaultMetadata.java index 036ced33d..5eeed6742 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/DefaultMetadata.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/DefaultMetadata.java @@ -19,6 +19,7 @@ package org.eclipse.aether.metadata; import java.io.File; +import java.nio.file.Path; import java.util.Map; import static java.util.Objects.requireNonNull; @@ -39,7 +40,7 @@ public final class DefaultMetadata extends AbstractMetadata { private final Nature nature; - private final File file; + private final Path path; private final Map properties; @@ -50,7 +51,7 @@ public final class DefaultMetadata extends AbstractMetadata { * @param nature The nature of the metadata, must not be {@code null}. */ public DefaultMetadata(String type, Nature nature) { - this("", "", "", type, nature, null, (File) null); + this("", "", "", type, nature, null, (Path) null); } /** @@ -61,7 +62,7 @@ public DefaultMetadata(String type, Nature nature) { * @param nature The nature of the metadata, must not be {@code null}. */ public DefaultMetadata(String groupId, String type, Nature nature) { - this(groupId, "", "", type, nature, null, (File) null); + this(groupId, "", "", type, nature, null, (Path) null); } /** @@ -73,7 +74,7 @@ public DefaultMetadata(String groupId, String type, Nature nature) { * @param nature The nature of the metadata, must not be {@code null}. */ public DefaultMetadata(String groupId, String artifactId, String type, Nature nature) { - this(groupId, artifactId, "", type, nature, null, (File) null); + this(groupId, artifactId, "", type, nature, null, (Path) null); } /** @@ -86,7 +87,7 @@ public DefaultMetadata(String groupId, String artifactId, String type, Nature na * @param nature The nature of the metadata, must not be {@code null}. */ public DefaultMetadata(String groupId, String artifactId, String version, String type, Nature nature) { - this(groupId, artifactId, version, type, nature, null, (File) null); + this(groupId, artifactId, version, type, nature, null, (Path) null); } /** @@ -98,11 +99,28 @@ public DefaultMetadata(String groupId, String artifactId, String version, String * @param type The type of the metadata, e.g. "maven-metadata.xml", may be {@code null}. * @param nature The nature of the metadata, must not be {@code null}. * @param file The resolved file of the metadata, may be {@code null}. + * @deprecated Use {@link #DefaultMetadata(String, String, String, String, Nature, Path)} instead. */ + @Deprecated public DefaultMetadata(String groupId, String artifactId, String version, String type, Nature nature, File file) { this(groupId, artifactId, version, type, nature, null, file); } + /** + * Creates a new metadata for the groupId:artifactId:version level with the specific type and nature. + * + * @param groupId The group identifier to which this metadata applies, may be {@code null}. + * @param artifactId The artifact identifier to which this metadata applies, may be {@code null}. + * @param version The version to which this metadata applies, may be {@code null}. + * @param type The type of the metadata, e.g. "maven-metadata.xml", may be {@code null}. + * @param nature The nature of the metadata, must not be {@code null}. + * @param path The resolved file of the metadata, may be {@code null}. + * @since 2.0.0 + */ + public DefaultMetadata(String groupId, String artifactId, String version, String type, Nature nature, Path path) { + this(groupId, artifactId, version, type, nature, null, path); + } + /** * Creates a new metadata for the groupId:artifactId:version level with the specific type and nature. * @@ -113,7 +131,9 @@ public DefaultMetadata(String groupId, String artifactId, String version, String * @param nature The nature of the metadata, must not be {@code null}. * @param properties The properties of the metadata, may be {@code null} if none. * @param file The resolved file of the metadata, may be {@code null}. + * @deprecated Use {@link #DefaultMetadata(String, String, String, String, Nature, Map, Path)} instead. */ + @Deprecated public DefaultMetadata( String groupId, String artifactId, @@ -127,7 +147,36 @@ public DefaultMetadata( this.version = emptify(version); this.type = emptify(type); this.nature = requireNonNull(nature, "metadata nature cannot be null"); - this.file = file; + this.path = file != null ? file.toPath() : null; + this.properties = copyProperties(properties); + } + + /** + * Creates a new metadata for the groupId:artifactId:version level with the specific type and nature. + * + * @param groupId The group identifier to which this metadata applies, may be {@code null}. + * @param artifactId The artifact identifier to which this metadata applies, may be {@code null}. + * @param version The version to which this metadata applies, may be {@code null}. + * @param type The type of the metadata, e.g. "maven-metadata.xml", may be {@code null}. + * @param nature The nature of the metadata, must not be {@code null}. + * @param properties The properties of the metadata, may be {@code null} if none. + * @param path The resolved file of the metadata, may be {@code null}. + * @since 2.0.0 + */ + public DefaultMetadata( + String groupId, + String artifactId, + String version, + String type, + Nature nature, + Map properties, + Path path) { + this.groupId = emptify(groupId); + this.artifactId = emptify(artifactId); + this.version = emptify(version); + this.type = emptify(type); + this.nature = requireNonNull(nature, "metadata nature cannot be null"); + this.path = path; this.properties = copyProperties(properties); } @@ -137,7 +186,7 @@ public DefaultMetadata( String version, String type, Nature nature, - File file, + Path path, Map properties) { // NOTE: This constructor assumes immutability of the provided properties, for internal use only this.groupId = emptify(groupId); @@ -145,7 +194,7 @@ public DefaultMetadata( this.version = emptify(version); this.type = emptify(type); this.nature = nature; - this.file = file; + this.path = path; this.properties = properties; } @@ -153,30 +202,43 @@ private static String emptify(String str) { return (str == null) ? "" : str; } + @Override public String getGroupId() { return groupId; } + @Override public String getArtifactId() { return artifactId; } + @Override public String getVersion() { return version; } + @Override public String getType() { return type; } + @Override public Nature getNature() { return nature; } + @Deprecated + @Override public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + @Override + public Path getPath() { + return path; } + @Override public Map getProperties() { return properties; } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/MergeableMetadata.java b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/MergeableMetadata.java index 96ab2ee9c..54c96a1f0 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/MergeableMetadata.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/MergeableMetadata.java @@ -19,6 +19,7 @@ package org.eclipse.aether.metadata; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.RepositoryException; @@ -34,9 +35,24 @@ public interface MergeableMetadata extends Metadata { * @param current The path to the current metadata file, may not exist but must not be {@code null}. * @param result The path to the result file where the merged metadata should be stored, must not be {@code null}. * @throws RepositoryException If the metadata could not be merged. + * @deprecated Use {@link #merge(Path, Path)} instead. */ + @Deprecated void merge(File current, File result) throws RepositoryException; + /** + * Merges this metadata into the current metadata (if any). Note that this method will be invoked regardless whether + * metadata currently exists or not. + * + * @param current The path to the current metadata file, may not exist but must not be {@code null}. + * @param result The path to the result file where the merged metadata should be stored, must not be {@code null}. + * @throws RepositoryException If the metadata could not be merged. + * @since 2.0.0 + */ + default void merge(Path current, Path result) throws RepositoryException { + merge(current.toFile(), result.toFile()); + } + /** * Indicates whether this metadata has been merged. * diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/Metadata.java b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/Metadata.java index e957919a1..fa9f84d1d 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/Metadata.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/metadata/Metadata.java @@ -19,6 +19,7 @@ package org.eclipse.aether.metadata; import java.io.File; +import java.nio.file.Path; import java.util.Map; /** @@ -96,17 +97,38 @@ enum Nature { * Gets the file of this metadata. Note that only resolved metadata has a file associated with it. * * @return The file or {@code null} if none. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated File getFile(); + /** + * Gets the file of this metadata. Note that only resolved metadata has a file associated with it. + * + * @return The file or {@code null} if none. + * @since 2.0.0 + */ + Path getPath(); + /** * Sets the file of the metadata. * * @param file The file of the metadata, may be {@code null} * @return The new metadata, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated Metadata setFile(File file); + /** + * Sets the file of the metadata. + * + * @param path The file of the metadata, may be {@code null} + * @return The new metadata, never {@code null}. + * @since 2.0.0 + */ + Metadata setPath(Path path); + /** * Gets the specified property. * diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java index aa1636b3c..02869c15d 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java @@ -19,6 +19,7 @@ package org.eclipse.aether.repository; import java.io.File; +import java.nio.file.Path; import static java.util.Objects.requireNonNull; @@ -31,7 +32,7 @@ public final class LocalArtifactResult { private final LocalArtifactRequest request; - private File file; + private Path path; private boolean available; @@ -61,9 +62,23 @@ public LocalArtifactRequest getRequest() { * remote repository that is not part of the list of remote repositories used for the query. * * @return The file to the requested artifact or {@code null} if the artifact does not exist locally. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the file to the requested artifact. Note that this file must not be used unless {@link #isAvailable()} + * returns {@code true}. An artifact file can be found but considered unavailable if the artifact was cached from a + * remote repository that is not part of the list of remote repositories used for the query. + * + * @return The file to the requested artifact or {@code null} if the artifact does not exist locally. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -71,9 +86,22 @@ public File getFile() { * * @param file The artifact file, may be {@code null}. * @return This result for chaining, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public LocalArtifactResult setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the file to requested artifact. + * + * @param path The artifact file, may be {@code null}. + * @return This result for chaining, never {@code null}. + * @since 2.0.0 + */ + public LocalArtifactResult setPath(Path path) { + this.path = path; return this; } @@ -126,6 +154,6 @@ public LocalArtifactResult setRepository(RemoteRepository repository) { @Override public String toString() { - return getFile() + " (" + (isAvailable() ? "available" : "unavailable") + ")"; + return getPath() + " (" + (isAvailable() ? "available" : "unavailable") + ")"; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java index a3ad6f473..7faf9fc27 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java @@ -19,6 +19,7 @@ package org.eclipse.aether.repository; import java.io.File; +import java.nio.file.Path; import static java.util.Objects.requireNonNull; @@ -31,7 +32,7 @@ public final class LocalMetadataResult { private final LocalMetadataRequest request; - private File file; + private Path path; private boolean stale; @@ -57,9 +58,21 @@ public LocalMetadataRequest getRequest() { * Gets the file to the requested metadata if the metadata is available in the local repository. * * @return The file to the requested metadata or {@code null}. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the file to the requested metadata if the metadata is available in the local repository. + * + * @return The file to the requested metadata or {@code null}. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -67,9 +80,22 @@ public File getFile() { * * @param file The metadata file, may be {@code null}. * @return This result for chaining, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public LocalMetadataResult setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the file to requested metadata. + * + * @param path The metadata file, may be {@code null}. + * @return This result for chaining, never {@code null}. + * @since 2.0.0 + */ + public LocalMetadataResult setPath(Path path) { + this.path = path; return this; } @@ -95,6 +121,6 @@ public LocalMetadataResult setStale(boolean stale) { @Override public String toString() { - return request.toString() + "(" + getFile() + ")"; + return request.toString() + "(" + getPath() + ")"; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java index 04ec5ad8e..44ae964b0 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java @@ -19,6 +19,9 @@ package org.eclipse.aether.repository; import java.io.File; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; /** @@ -29,7 +32,7 @@ */ public final class LocalRepository implements ArtifactRepository { - private final File basedir; + private final Path basePath; private final String type; @@ -39,33 +42,71 @@ public final class LocalRepository implements ArtifactRepository { * @param basedir The base directory of the repository, may be {@code null}. */ public LocalRepository(String basedir) { - this((basedir != null) ? new File(basedir) : null, ""); + this.basePath = Paths.get(RepositoryUriUtils.toUri(basedir)).toAbsolutePath(); + this.type = ""; } /** * Creates a new local repository with the specified base directory and unknown type. * * @param basedir The base directory of the repository, may be {@code null}. + * @since 2.0.0 */ + public LocalRepository(URI basedir) { + this((basedir != null) ? Paths.get(basedir) : null, ""); + } + + /** + * Creates a new local repository with the specified base directory and unknown type. + * + * @param basedir The base directory of the repository, may be {@code null}. + * @deprecated Use {@link #LocalRepository(Path)} instead. + */ + @Deprecated public LocalRepository(File basedir) { this(basedir, ""); } + /** + * Creates a new local repository with the specified base directory and unknown type. + * + * @param basePath The base directory of the repository, may be {@code null}. + * @since 2.0.0 + */ + public LocalRepository(Path basePath) { + this(basePath, ""); + } + /** * Creates a new local repository with the specified properties. * * @param basedir The base directory of the repository, may be {@code null}. * @param type The type of the repository, may be {@code null}. + * @deprecated Use {@link #LocalRepository(Path, String)} instead. */ + @Deprecated public LocalRepository(File basedir, String type) { - this.basedir = basedir; + this(basedir != null ? basedir.toPath() : null, type); + } + + /** + * Creates a new local repository with the specified properties. + * + * @param basePath The base directory of the repository, may be {@code null}. + * @param type The type of the repository, may be {@code null}. + * @since 2.0.0 + */ + public LocalRepository(Path basePath, String type) { + this.basePath = basePath; this.type = (type != null) ? type : ""; } + @Override public String getContentType() { return type; } + @Override public String getId() { return "local"; } @@ -74,14 +115,26 @@ public String getId() { * Gets the base directory of the repository. * * @return The base directory or {@code null} if none. + * @deprecated Use {@link #getBasePath()} instead. */ + @Deprecated public File getBasedir() { - return basedir; + return basePath != null ? basePath.toFile() : null; + } + + /** + * Gets the base directory of the repository. + * + * @return The base directory or {@code null} if none. + * @since 2.0.0 + */ + public Path getBasePath() { + return basePath; } @Override public String toString() { - return getBasedir() + " (" + getContentType() + ")"; + return getBasePath() + " (" + getContentType() + ")"; } @Override @@ -95,13 +148,13 @@ public boolean equals(Object obj) { LocalRepository that = (LocalRepository) obj; - return Objects.equals(basedir, that.basedir) && Objects.equals(type, that.type); + return Objects.equals(basePath, that.basePath) && Objects.equals(type, that.type); } @Override public int hashCode() { int hash = 17; - hash = hash * 31 + hash(basedir); + hash = hash * 31 + hash(basePath); hash = hash * 31 + hash(type); return hash; } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java index 66c348730..055804068 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java @@ -72,7 +72,7 @@ public NoLocalRepositoryManagerException(LocalRepository repository, String mess private static String toMessage(LocalRepository repository) { if (repository != null) { return "No manager available for local repository (" - + repository.getBasedir().getAbsolutePath() + ") of type " + repository.getContentType(); + + repository.getBasePath().toAbsolutePath() + ") of type " + repository.getContentType(); } else { return "No manager available for local repository"; } diff --git a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/PathUtils.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryUriUtils.java similarity index 79% rename from maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/PathUtils.java rename to maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryUriUtils.java index 5b0813b68..6fa085269 100644 --- a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/PathUtils.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryUriUtils.java @@ -16,23 +16,39 @@ * specific language governing permissions and limitations * under the License. */ -package org.eclipse.aether.transport.file; +package org.eclipse.aether.repository; + +import java.io.File; +import java.net.URI; /** * URL handling for file URLs. Based on org.apache.maven.wagon.PathUtils. + * + * @since 2.0.0 */ -final class PathUtils { +public final class RepositoryUriUtils { + + private RepositoryUriUtils() {} - private PathUtils() {} + public static URI toUri(String repositoryUrl) { + final String protocol = protocol(repositoryUrl); + if ("file".equals(protocol) + || protocol.isEmpty() + || protocol.length() == 1 + && (Character.isLetter(protocol.charAt(0)) && Character.isUpperCase(protocol.charAt(0)))) { + return new File(basedir(repositoryUrl)).toURI(); + } else { + return URI.create(repositoryUrl); + } + } /** - * Return the protocol name.
- * E.g: for input http://www.codehause.org this method will return http + * Return the protocol name. * * @param url the url - * @return the host name + * @return the protocol or empty string. */ - public static String protocol(final String url) { + private static String protocol(final String url) { final int pos = url.indexOf(":"); if (pos == -1) { @@ -47,10 +63,10 @@ public static String protocol(final String url) { * @param url the file-repository URL * @return the basedir of the repository */ - public static String basedir(String url) { - String protocol = PathUtils.protocol(url); + private static String basedir(String url) { + String protocol = protocol(url); - String retValue = null; + String retValue; if (!protocol.isEmpty()) { retValue = url.substring(protocol.length() + 1); @@ -97,7 +113,7 @@ public static String basedir(String url) { * @param url The URL to decode, may be null. * @return The decoded URL or null if the input was null. */ - static String decode(String url) { + private static String decode(String url) { String decoded = url; if (url != null) { int pos = -1; diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceReader.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceReader.java index 92127bbe9..5a55e5f14 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceReader.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceReader.java @@ -19,6 +19,7 @@ package org.eclipse.aether.repository; import java.io.File; +import java.nio.file.Path; import java.util.List; import org.eclipse.aether.artifact.Artifact; @@ -45,6 +46,18 @@ public interface WorkspaceReader { */ File findArtifact(Artifact artifact); + /** + * Locates the specified artifact. + * + * @param artifact The artifact to locate, must not be {@code null}. + * @return The path to the artifact or {@code null} if the artifact is not available. + * @since 2.0.0 + */ + default Path findArtifactPath(Artifact artifact) { + File file = findArtifact(artifact); + return file != null ? file.toPath() : null; + } + /** * Determines all available versions of the specified artifact. * diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResolutionException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResolutionException.java index a45ea5798..0ec971b21 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResolutionException.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResolutionException.java @@ -102,7 +102,7 @@ private static String getMessage(List results) { LocalArtifactResult localResult = result.getLocalArtifactResult(); if (localResult != null) { buffer.append(" ("); - if (localResult.getFile() != null) { + if (localResult.getPath() != null) { buffer.append("present"); if (!localResult.isAvailable()) { buffer.append(", but unavailable"); diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResult.java index 3ee3d0465..d155d09d1 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResult.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/ArtifactResult.java @@ -34,7 +34,7 @@ * The result of an artifact resolution request. * * @see RepositorySystem#resolveArtifacts(org.eclipse.aether.RepositorySystemSession, java.util.Collection) - * @see Artifact#getFile() + * @see Artifact#getPath() */ public final class ArtifactResult { @@ -217,10 +217,10 @@ public void setLocalArtifactResult(LocalArtifactResult localArtifactResult) { * of the specified remote repositories. * * @return {@code true} if the artifact was resolved, {@code false} otherwise. - * @see Artifact#getFile() + * @see Artifact#getPath() */ public boolean isResolved() { - return getArtifact() != null && getArtifact().getFile() != null; + return getArtifact() != null && getArtifact().getPath() != null; } /** diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/MetadataResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/MetadataResult.java index ddb648c39..f9155414d 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/MetadataResult.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/resolution/MetadataResult.java @@ -127,10 +127,10 @@ public boolean isUpdated() { * refetch the metadata from the remote repository. * * @return {@code true} if the metadata was resolved, {@code false} otherwise. - * @see Metadata#getFile() + * @see Metadata#getPath() */ public boolean isResolved() { - return getMetadata() != null && getMetadata().getFile() != null; + return getMetadata() != null && getMetadata().getPath() != null; } /** diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/MetadataNotFoundException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/MetadataNotFoundException.java index f7f9c2d94..fdc2093f4 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/MetadataNotFoundException.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/MetadataNotFoundException.java @@ -41,7 +41,7 @@ private static String getString(String prefix, LocalRepository repository) { if (repository == null) { return ""; } else { - return prefix + repository.getId() + " (" + repository.getBasedir() + ")"; + return prefix + repository.getId() + " (" + repository.getBasePath().toAbsolutePath() + ")"; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java index e2abb67c1..824c40af2 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java @@ -19,6 +19,7 @@ package org.eclipse.aether.transfer; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.RequestTrace; @@ -33,7 +34,7 @@ public final class TransferResource { private final String resourceName; - private final File file; + private final Path path; private final long startTime; @@ -56,9 +57,30 @@ public final class TransferResource { * @param trace The trace information, may be {@code null}. * * @since 1.1.0 + * @deprecated Use {@link TransferResource(String, String, String, Path, RequestTrace)} instead. */ + @Deprecated public TransferResource( String repositoryId, String repositoryUrl, String resourceName, File file, RequestTrace trace) { + this(repositoryId, repositoryUrl, resourceName, file != null ? file.toPath() : null, trace); + } + + /** + * Creates a new transfer resource with the specified properties. + * + * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or + * empty if unknown. + * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a + * trailing slash will automatically be added if missing. + * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash + * (if any) will be automatically removed. + * @param path The source/target file involved in the transfer, may be {@code null}. + * @param trace The trace information, may be {@code null}. + * + * @since 2.0.0 + */ + public TransferResource( + String repositoryId, String repositoryUrl, String resourceName, Path path, RequestTrace trace) { if (repositoryId == null || repositoryId.isEmpty()) { this.repositoryId = ""; } else { @@ -81,7 +103,7 @@ public TransferResource( this.resourceName = resourceName; } - this.file = file; + this.path = path; this.trace = trace; @@ -100,7 +122,7 @@ public String getRepositoryId() { } /** - * The base URL of the repository, e.g. "http://repo1.maven.org/maven2/". Unless the URL is unknown, it will be + * The base URL of the repository, e.g. "https://repo1.maven.org/maven2/". Unless the URL is unknown, it will be * terminated by a trailing slash. * * @return The base URL of the repository or an empty string if unknown, never {@code null}. @@ -123,9 +145,22 @@ public String getResourceName() { * remote resource, no local file will be involved in the transfer. * * @return The source/target file involved in the transfer or {@code null} if none. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the local file being uploaded or downloaded. When the repository system merely checks for the existence of a + * remote resource, no local file will be involved in the transfer. + * + * @return The source/target file involved in the transfer or {@code null} if none. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -195,6 +230,6 @@ public RequestTrace getTrace() { @Override public String toString() { - return getRepositoryUrl() + getResourceName() + " <> " + getFile(); + return getRepositoryUrl() + getResourceName() + " <> " + getPath(); } } diff --git a/maven-resolver-api/src/test/java/org/eclipse/aether/repository/LocalRepositoryTest.java b/maven-resolver-api/src/test/java/org/eclipse/aether/repository/LocalRepositoryTest.java new file mode 100644 index 000000000..ce45c8cbf --- /dev/null +++ b/maven-resolver-api/src/test/java/org/eclipse/aether/repository/LocalRepositoryTest.java @@ -0,0 +1,52 @@ +/* + * 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.repository; + +import java.io.File; +import java.nio.file.FileSystems; + +import org.junit.jupiter.api.Test; + +public class LocalRepositoryTest { + @Test + void cwd() { + doIt(new File(".")); + } + + @Test + void allRoots() { + FileSystems.getDefault().getRootDirectories().forEach(p -> doIt(new File(p.toUri()))); + } + + @Test + void root() { + doIt(new File(File.separator)); + } + + @Test + void allSomePaths() { + FileSystems.getDefault() + .getRootDirectories() + .forEach(p -> doIt(new File(p.resolve("some/path").toUri()))); + } + + private void doIt(File file) { + new LocalRepository(file.toPath().toUri().toASCIIString()); + } +} diff --git a/maven-resolver-api/src/test/java/org/eclipse/aether/transfer/TransferEventTest.java b/maven-resolver-api/src/test/java/org/eclipse/aether/transfer/TransferEventTest.java index d9fd517f7..6b4c0fe64 100644 --- a/maven-resolver-api/src/test/java/org/eclipse/aether/transfer/TransferEventTest.java +++ b/maven-resolver-api/src/test/java/org/eclipse/aether/transfer/TransferEventTest.java @@ -19,6 +19,7 @@ package org.eclipse.aether.transfer; import java.nio.ByteBuffer; +import java.nio.file.Path; import org.eclipse.aether.RepositorySystemSession; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ */ public class TransferEventTest { - private static final TransferResource res = new TransferResource("none", "file://nil", "void", null, null); + private static final TransferResource res = new TransferResource("none", "file://nil", "void", (Path) null, null); private static final RepositorySystemSession session = mock(RepositorySystemSession.class); 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 b699ffa77..572f2a622 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 @@ -18,10 +18,10 @@ */ package org.eclipse.aether.connector.basic; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -51,7 +51,7 @@ import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.Transporter; import org.eclipse.aether.spi.connector.transport.TransporterProvider; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.NoRepositoryConnectorException; import org.eclipse.aether.transfer.NoRepositoryLayoutException; @@ -83,7 +83,7 @@ final class BasicRepositoryConnector implements RepositoryConnector { private final Map providedChecksumsSources; - private final FileProcessor fileProcessor; + private final ChecksumProcessor checksumProcessor; private final RemoteRepository repository; @@ -113,7 +113,7 @@ final class BasicRepositoryConnector implements RepositoryConnector { TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, ChecksumPolicyProvider checksumPolicyProvider, - FileProcessor fileProcessor, + ChecksumProcessor checksumProcessor, Map providedChecksumsSources) throws NoRepositoryConnectorException { try { @@ -130,7 +130,7 @@ final class BasicRepositoryConnector implements RepositoryConnector { this.session = session; this.repository = repository; - this.fileProcessor = fileProcessor; + this.checksumProcessor = checksumProcessor; this.providedChecksumsSources = providedChecksumsSources; this.closed = new AtomicBoolean(false); @@ -191,7 +191,7 @@ public void get( for (MetadataDownload transfer : safeMetadataDownloads) { URI location = layout.getLocation(transfer.getMetadata(), false); - TransferResource resource = newTransferResource(location, transfer.getFile(), transfer.getTrace()); + TransferResource resource = newTransferResource(location, transfer.getPath(), transfer.getTrace()); TransferEvent.Builder builder = newEventBuilder(resource, false, false); MetadataTransportListener listener = new MetadataTransportListener(transfer, repository, builder); @@ -203,7 +203,7 @@ public void get( Runnable task = new GetTaskRunner( location, - transfer.getFile(), + transfer.getPath(), checksumPolicy, checksumAlgorithmFactories, checksumLocations, @@ -231,7 +231,7 @@ public void get( URI location = layout.getLocation(transfer.getArtifact(), false); - TransferResource resource = newTransferResource(location, transfer.getFile(), transfer.getTrace()); + TransferResource resource = newTransferResource(location, transfer.getPath(), transfer.getTrace()); TransferEvent.Builder builder = newEventBuilder(resource, false, transfer.isExistenceCheck()); ArtifactTransportListener listener = new ArtifactTransportListener(transfer, repository, builder); @@ -247,7 +247,7 @@ public void get( task = new GetTaskRunner( location, - transfer.getFile(), + transfer.getPath(), checksumPolicy, checksumAlgorithmFactories, checksumLocations, @@ -282,14 +282,14 @@ public void put( for (ArtifactUpload transfer : safeArtifactUploads) { URI location = layout.getLocation(transfer.getArtifact(), true); - TransferResource resource = newTransferResource(location, transfer.getFile(), transfer.getTrace()); + TransferResource resource = newTransferResource(location, transfer.getPath(), transfer.getTrace()); TransferEvent.Builder builder = newEventBuilder(resource, true, false); ArtifactTransportListener listener = new ArtifactTransportListener(transfer, repository, builder); List checksumLocations = layout.getChecksumLocations(transfer.getArtifact(), true, location); - Runnable task = new PutTaskRunner(location, transfer.getFile(), checksumLocations, listener); + Runnable task = new PutTaskRunner(location, transfer.getPath(), checksumLocations, listener); if (first) { task.run(); first = false; @@ -304,14 +304,14 @@ public void put( for (MetadataUpload transfer : transferGroup) { URI location = layout.getLocation(transfer.getMetadata(), true); - TransferResource resource = newTransferResource(location, transfer.getFile(), transfer.getTrace()); + TransferResource resource = newTransferResource(location, transfer.getPath(), transfer.getTrace()); TransferEvent.Builder builder = newEventBuilder(resource, true, false); MetadataTransportListener listener = new MetadataTransportListener(transfer, repository, builder); List checksumLocations = layout.getChecksumLocations(transfer.getMetadata(), true, location); - Runnable task = new PutTaskRunner(location, transfer.getFile(), checksumLocations, listener); + Runnable task = new PutTaskRunner(location, transfer.getPath(), checksumLocations, listener); if (first) { task.run(); first = false; @@ -368,7 +368,7 @@ private static Collection safe(Collection items) { return (items != null) ? items : Collections.emptyList(); } - private TransferResource newTransferResource(URI path, File file, RequestTrace trace) { + private TransferResource newTransferResource(URI path, Path file, RequestTrace trace) { return new TransferResource(repository.getId(), repository.getUrl(), path.toString(), file, trace); } @@ -432,13 +432,13 @@ protected void runTask() throws Exception { class GetTaskRunner extends TaskRunner implements ChecksumValidator.ChecksumFetcher { - private final File file; + private final Path file; private final ChecksumValidator checksumValidator; GetTaskRunner( URI path, - File file, + Path file, ChecksumPolicy checksumPolicy, List checksumAlgorithmFactories, List checksumLocations, @@ -449,7 +449,7 @@ class GetTaskRunner extends TaskRunner implements ChecksumValidator.ChecksumFetc checksumValidator = new ChecksumValidator( file, checksumAlgorithmFactories, - fileProcessor, + checksumProcessor, this, checksumPolicy, providedChecksums, @@ -457,9 +457,9 @@ class GetTaskRunner extends TaskRunner implements ChecksumValidator.ChecksumFetc } @Override - public boolean fetchChecksum(URI remote, File local) throws Exception { + public boolean fetchChecksum(URI remote, Path local) throws Exception { try { - transporter.get(new GetTask(remote).setDataFile(local)); + transporter.get(new GetTask(remote).setDataPath(local)); } catch (Exception e) { if (transporter.classify(e) == Transporter.ERROR_NOT_FOUND) { return false; @@ -471,11 +471,11 @@ public boolean fetchChecksum(URI remote, File local) throws Exception { @Override protected void runTask() throws Exception { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(file.toPath())) { - final File tmp = tempFile.getPath().toFile(); + try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(file)) { + final Path tmp = tempFile.getPath(); listener.setChecksumCalculator(checksumValidator.newChecksumCalculator(tmp)); for (int firstTrial = 0, lastTrial = 1, trial = firstTrial; ; trial++) { - GetTask task = new GetTask(path).setDataFile(tmp, false).setListener(listener); + GetTask task = new GetTask(path).setDataPath(tmp, false).setListener(listener); transporter.get(task); try { checksumValidator.validate( @@ -504,13 +504,13 @@ protected void runTask() throws Exception { class PutTaskRunner extends TaskRunner { - private final File file; + private final Path file; private final Collection checksumLocations; PutTaskRunner( URI path, - File file, + Path file, List checksumLocations, TransferTransportListener listener) { super(path, listener); @@ -521,15 +521,15 @@ class PutTaskRunner extends TaskRunner { @SuppressWarnings("checkstyle:innerassignment") @Override protected void runTask() throws Exception { - transporter.put(new PutTask(path).setDataFile(file).setListener(listener)); + transporter.put(new PutTask(path).setDataPath(file).setListener(listener)); uploadChecksums(file, null); } /** - * @param file source + * @param path source * @param bytes transformed data from file or {@code null} */ - private void uploadChecksums(File file, byte[] bytes) { + private void uploadChecksums(Path path, byte[] bytes) { if (checksumLocations.isEmpty()) { return; } @@ -543,7 +543,7 @@ private void uploadChecksums(File file, byte[] bytes) { if (bytes != null) { sumsByAlgo = ChecksumAlgorithmHelper.calculate(bytes, algorithms); } else { - sumsByAlgo = ChecksumAlgorithmHelper.calculate(file, algorithms); + sumsByAlgo = ChecksumAlgorithmHelper.calculate(path, algorithms); } for (RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations) { 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 826995193..b16610e87 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 @@ -31,7 +31,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.spi.connector.transport.TransporterProvider; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.transfer.NoRepositoryConnectorException; import static java.util.Objects.requireNonNull; @@ -50,7 +50,7 @@ public final class BasicRepositoryConnectorFactory implements RepositoryConnecto private final ChecksumPolicyProvider checksumPolicyProvider; - private final FileProcessor fileProcessor; + private final ChecksumProcessor checksumProcessor; private final Map providedChecksumsSources; @@ -61,12 +61,12 @@ public BasicRepositoryConnectorFactory( TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, ChecksumPolicyProvider checksumPolicyProvider, - FileProcessor fileProcessor, + 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.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null"); + this.checksumProcessor = requireNonNull(checksumProcessor, "checksum processor cannot be null"); this.providedChecksumsSources = requireNonNull(providedChecksumsSources, "provided checksum sources cannot be null"); } @@ -99,7 +99,7 @@ public RepositoryConnector newInstance(RepositorySystemSession session, RemoteRe transporterProvider, layoutProvider, checksumPolicyProvider, - fileProcessor, + checksumProcessor, providedChecksumsSources); } } diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java index 94fe01fe8..aa8921217 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java @@ -19,12 +19,12 @@ package org.eclipse.aether.connector.basic; import java.io.BufferedInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -78,17 +78,17 @@ public Object get() { private final List checksums; - private final File targetFile; + private final Path targetFile; public static ChecksumCalculator newInstance( - File targetFile, Collection checksumAlgorithmFactories) { + Path targetFile, Collection checksumAlgorithmFactories) { if (checksumAlgorithmFactories == null || checksumAlgorithmFactories.isEmpty()) { return null; } return new ChecksumCalculator(targetFile, checksumAlgorithmFactories); } - private ChecksumCalculator(File targetFile, Collection checksumAlgorithmFactories) { + private ChecksumCalculator(Path targetFile, Collection checksumAlgorithmFactories) { this.checksums = new ArrayList<>(); Set algos = new HashSet<>(); for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) { @@ -107,7 +107,7 @@ public void init(long dataOffset) { return; } - try (InputStream in = new BufferedInputStream(Files.newInputStream(targetFile.toPath()))) { + try (InputStream in = new BufferedInputStream(Files.newInputStream(targetFile))) { long total = 0; final byte[] buffer = new byte[1024 * 32]; while (total < dataOffset) { 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 6cfb425da..24fdbc3a1 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 @@ -18,10 +18,10 @@ */ package org.eclipse.aether.connector.basic; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; +import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -30,7 +30,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.ChecksumLocation; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; @@ -47,16 +47,16 @@ interface ChecksumFetcher { * Fetches the checksums from remote location into provided local file. The checksums fetched in this way * are of kind {@link ChecksumKind#REMOTE_EXTERNAL}. */ - boolean fetchChecksum(URI remote, File local) throws Exception; + boolean fetchChecksum(URI remote, Path local) throws Exception; } private static final Logger LOGGER = LoggerFactory.getLogger(ChecksumValidator.class); - private final File dataFile; + private final Path dataPath; private final Collection checksumAlgorithmFactories; - private final FileProcessor fileProcessor; + private final ChecksumProcessor checksumProcessor; private final ChecksumFetcher checksumFetcher; @@ -66,19 +66,19 @@ interface ChecksumFetcher { private final Collection checksumLocations; - private final Map checksumExpectedValues; + private final Map checksumExpectedValues; ChecksumValidator( - File dataFile, + Path dataPath, Collection checksumAlgorithmFactories, - FileProcessor fileProcessor, + ChecksumProcessor checksumProcessor, ChecksumFetcher checksumFetcher, ChecksumPolicy checksumPolicy, Map providedChecksums, Collection checksumLocations) { - this.dataFile = dataFile; + this.dataPath = dataPath; this.checksumAlgorithmFactories = checksumAlgorithmFactories; - this.fileProcessor = fileProcessor; + this.checksumProcessor = checksumProcessor; this.checksumFetcher = checksumFetcher; this.checksumPolicy = checksumPolicy; this.providedChecksums = providedChecksums; @@ -86,7 +86,7 @@ interface ChecksumFetcher { this.checksumExpectedValues = new HashMap<>(); } - public ChecksumCalculator newChecksumCalculator(File targetFile) { + public ChecksumCalculator newChecksumCalculator(Path targetFile) { if (checksumPolicy != null) { return ChecksumCalculator.newInstance(targetFile, checksumAlgorithmFactories); } @@ -131,7 +131,7 @@ private boolean validateChecksums(Map actualChecksums, ChecksumKind k String actual = String.valueOf(calculated); String expected = entry.getValue().toString(); - checksumExpectedValues.put(getChecksumFile(checksumAlgorithmFactory), expected); + checksumExpectedValues.put(getChecksumPath(checksumAlgorithmFactory), expected); if (!isEqualChecksum(expected, actual)) { checksumPolicy.onChecksumMismatch( @@ -155,9 +155,9 @@ private boolean validateExternalChecksums(Map actualChecksums) throws calculated)); continue; } - File checksumFile = getChecksumFile(checksumLocation.getChecksumAlgorithmFactory()); + Path checksumFile = getChecksumPath(checksumLocation.getChecksumAlgorithmFactory()); try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) { - File tmp = tempFile.getPath().toFile(); + Path tmp = tempFile.getPath(); try { if (!checksumFetcher.fetchChecksum(checksumLocation.getLocation(), tmp)) { continue; @@ -169,7 +169,7 @@ private boolean validateExternalChecksums(Map actualChecksums) throws } String actual = String.valueOf(calculated); - String expected = fileProcessor.readChecksum(tmp); + String expected = checksumProcessor.readChecksum(tmp); checksumExpectedValues.put(checksumFile, expected); if (!isEqualChecksum(expected, actual)) { @@ -192,8 +192,8 @@ private static boolean isEqualChecksum(String expected, String actual) { return expected.equalsIgnoreCase(actual); } - private File getChecksumFile(ChecksumAlgorithmFactory factory) { - return new File(dataFile.getPath() + '.' + factory.getFileExtension()); + private Path getChecksumPath(ChecksumAlgorithmFactory factory) { + return dataPath.getParent().resolve(dataPath.getFileName() + "." + factory.getFileExtension()); } public void retry() { @@ -206,10 +206,10 @@ public boolean handle(ChecksumFailureException exception) { } public void commit() { - for (Map.Entry entry : checksumExpectedValues.entrySet()) { - File checksumFile = entry.getKey(); + for (Map.Entry entry : checksumExpectedValues.entrySet()) { + Path checksumFile = entry.getKey(); try { - fileProcessor.writeChecksum(checksumFile, entry.getValue()); + checksumProcessor.writeChecksum(checksumFile, entry.getValue()); } catch (IOException e) { LOGGER.debug("Failed to write checksum file {}", checksumFile, e); throw new UncheckedIOException(e); diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java index 9641bfc1f..6ccfe0edb 100644 --- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java +++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java @@ -48,7 +48,7 @@ private ChecksumCalculator newCalculator(String... algos) { for (String algo : algos) { checksumAlgorithmFactories.add(selector.select(algo)); } - return ChecksumCalculator.newInstance(file, checksumAlgorithmFactories); + return ChecksumCalculator.newInstance(file.toPath(), checksumAlgorithmFactories); } private ByteBuffer toBuffer(String data) { 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 27afc216c..6a8960866 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 @@ -21,6 +21,8 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -28,7 +30,7 @@ import java.util.List; import java.util.Map; -import org.eclipse.aether.internal.test.util.TestFileProcessor; +import org.eclipse.aether.internal.test.util.TestChecksumProcessor; import org.eclipse.aether.internal.test.util.TestFileUtils; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; @@ -113,12 +115,12 @@ private static class StubChecksumFetcher implements ChecksumValidator.ChecksumFe HashMap checksums = new HashMap<>(); - ArrayList checksumFiles = new ArrayList<>(); + ArrayList checksumFiles = new ArrayList<>(); private final ArrayList fetchedFiles = new ArrayList<>(); @Override - public boolean fetchChecksum(URI remote, File local) throws Exception { + public boolean fetchChecksum(URI remote, Path local) throws Exception { fetchedFiles.add(remote); Object checksum = checksums.get(remote); if (checksum == null) { @@ -127,7 +129,7 @@ public boolean fetchChecksum(URI remote, File local) throws Exception { if (checksum instanceof Exception) { throw (Exception) checksum; } - TestFileUtils.writeString(local, checksum.toString()); + TestFileUtils.writeString(local.toFile(), checksum.toString()); checksumFiles.add(local); return true; } @@ -185,9 +187,9 @@ private ChecksumValidator newValidator(String... factories) { private ChecksumValidator newValidator(Map providedChecksums, String... factories) { List checksumAlgorithmFactories = newChecksumAlgorithmFactories(factories); return new ChecksumValidator( - dataFile, + dataFile.toPath(), checksumAlgorithmFactories, - new TestFileProcessor(), + new TestChecksumProcessor(), fetcher, policy, providedChecksums, @@ -368,8 +370,8 @@ void testRetry_RemoveTempFiles() throws Exception { fetcher.assertFetchedFiles(SHA1); assertEquals(1, fetcher.checksumFiles.size()); validator.retry(); - for (File file : fetcher.checksumFiles) { - assertFalse(file.exists(), file.getAbsolutePath()); + for (Path file : fetcher.checksumFiles) { + assertFalse(Files.exists(file), file.toAbsolutePath().toString()); } } @@ -387,8 +389,8 @@ void testCommit_SaveChecksumFiles() throws Exception { checksumFile = new File(dataFile.getPath() + ".md5"); assertTrue(checksumFile.isFile(), checksumFile.getAbsolutePath()); assertEquals("bar", TestFileUtils.readString(checksumFile)); - for (File file : fetcher.checksumFiles) { - assertFalse(file.exists(), file.getAbsolutePath()); + for (Path file : fetcher.checksumFiles) { + assertFalse(Files.exists(file), file.toAbsolutePath().toString()); } } @@ -399,8 +401,8 @@ void testNoCommit_NoTempFiles() throws Exception { validator.validate(checksums(SHA1, "foo"), null); fetcher.assertFetchedFiles(SHA1); assertEquals(1, fetcher.checksumFiles.size()); - for (File file : fetcher.checksumFiles) { - assertFalse(file.exists(), file.getAbsolutePath()); + for (Path file : fetcher.checksumFiles) { + assertFalse(Files.exists(file), file.toAbsolutePath().toString()); } } } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/pom.xml b/maven-resolver-demos/maven-resolver-demo-snippets/pom.xml index 3cd6eb8f4..1e3c53e65 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/pom.xml +++ b/maven-resolver-demos/maven-resolver-demo-snippets/pom.xml @@ -97,6 +97,10 @@ org.eclipse.sisu org.eclipse.sisu.inject + + com.google.jimfs + jimfs + com.google.inject guice diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/DeployArtifacts.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/DeployArtifacts.java index e06f7b6e3..fcdd99644 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/DeployArtifacts.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/DeployArtifacts.java @@ -48,10 +48,10 @@ public static void main(String[] args) throws Exception { Booter.newRepositorySystemSession(system).build()) { Artifact jarArtifact = new DefaultArtifact("test", "org.apache.maven.aether.examples", "", "jar", "0.1-SNAPSHOT"); - jarArtifact = jarArtifact.setFile(new File("src/main/data/demo.jar")); + jarArtifact = jarArtifact.setPath(new File("src/main/data/demo.jar").toPath()); Artifact pomArtifact = new SubArtifact(jarArtifact, "", "pom"); - pomArtifact = pomArtifact.setFile(new File("pom.xml")); + pomArtifact = pomArtifact.setPath(new File("pom.xml").toPath()); RemoteRepository distRepo = new RemoteRepository.Builder( "org.apache.maven.aether.examples", diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/InstallArtifacts.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/InstallArtifacts.java index b06c34e19..d285f10b5 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/InstallArtifacts.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/InstallArtifacts.java @@ -47,10 +47,10 @@ public static void main(String[] args) throws Exception { Booter.newRepositorySystemSession(system).build()) { Artifact jarArtifact = new DefaultArtifact("test", "org.apache.maven.resolver.examples", "", "jar", "0.1-SNAPSHOT"); - jarArtifact = jarArtifact.setFile(new File("src/main/data/demo.jar")); + jarArtifact = jarArtifact.setPath(new File("src/main/data/demo.jar").toPath()); Artifact pomArtifact = new SubArtifact(jarArtifact, "", "pom"); - pomArtifact = pomArtifact.setFile(new File("pom.xml")); + pomArtifact = pomArtifact.setPath(new File("pom.xml").toPath()); InstallRequest installRequest = new InstallRequest(); installRequest.addArtifact(jarArtifact).addArtifact(pomArtifact); diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveArtifact.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveArtifact.java index cbc81d080..b41a10ab8 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveArtifact.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveArtifact.java @@ -20,7 +20,6 @@ import org.apache.maven.resolver.examples.util.Booter; import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; @@ -43,12 +42,12 @@ public static void main(String[] args) throws Exception { System.out.println(ResolveArtifact.class.getSimpleName()); try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) { - RepositorySystemSession.SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system); Artifact artifact; ArtifactRequest artifactRequest; ArtifactResult artifactResult; - try (CloseableSession session = sessionBuilder.build()) { + try (CloseableSession session = + Booter.newRepositorySystemSession(system).build()) { artifact = new DefaultArtifact("org.apache.maven.resolver:maven-resolver-util:1.3.3"); artifactRequest = new ArtifactRequest(); @@ -59,13 +58,13 @@ public static void main(String[] args) throws Exception { artifact = artifactResult.getArtifact(); - System.out.println(artifact + " resolved to " + artifact.getFile()); + System.out.println(artifact + " resolved to " + artifact.getPath()); } // signature - sessionBuilder.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_FAIL); - - try (CloseableSession session = sessionBuilder.build()) { + try (CloseableSession session = Booter.newRepositorySystemSession(system) + .setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_FAIL) + .build()) { artifact = new DefaultArtifact("org.apache.maven.resolver:maven-resolver-util:jar.asc:1.3.3"); artifactRequest = new ArtifactRequest(); @@ -76,7 +75,7 @@ public static void main(String[] args) throws Exception { artifact = artifactResult.getArtifact(); - System.out.println(artifact + " resolved signature to " + artifact.getFile()); + System.out.println(artifact + " resolved signature to " + artifact.getPath()); } } } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependencies.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependencies.java index 35c215b2c..a606c3524 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependencies.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ResolveTransitiveDependencies.java @@ -52,20 +52,20 @@ public static void main(String[] args) throws Exception { Booter.newRepositorySystemSession(system).build()) { Artifact artifact = new DefaultArtifact("org.apache.maven.resolver:maven-resolver-impl:1.3.3"); - DependencyFilter classpathFlter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE); + DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE); CollectRequest collectRequest = new CollectRequest(); collectRequest.setRoot(new Dependency(artifact, JavaScopes.COMPILE)); collectRequest.setRepositories(Booter.newRepositories(system, session)); - DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, classpathFlter); + DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, classpathFilter); List artifactResults = system.resolveDependencies(session, dependencyRequest).getArtifactResults(); for (ArtifactResult artifactResult : artifactResults) { System.out.println(artifactResult.getArtifact() + " resolved to " - + artifactResult.getArtifact().getFile()); + + artifactResult.getArtifact().getPath()); } } } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java index 7a9d95f22..b730e4289 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java @@ -22,7 +22,6 @@ import org.apache.maven.resolver.examples.util.ReverseTreeRepositoryListener; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession.CloseableSession; -import org.eclipse.aether.RepositorySystemSession.SessionBuilder; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.collection.CollectRequest; @@ -30,7 +29,6 @@ import org.eclipse.aether.resolution.ArtifactDescriptorResult; import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; import org.eclipse.aether.util.graph.transformer.ConflictResolver; -import org.eclipse.aether.util.listener.ChainedRepositoryListener; /** * Example of building reverse dependency tree using custom {@link ReverseTreeRepositoryListener}. @@ -47,14 +45,11 @@ public static void main(String[] args) throws Exception { System.out.println(ReverseDependencyTree.class.getSimpleName()); try (RepositorySystem system = Booter.newRepositorySystem(Booter.selectFactory(args))) { - SessionBuilder sessionBuilder = Booter.newRepositorySystemSession(system); - try (CloseableSession session = sessionBuilder.build()) { - sessionBuilder.setRepositoryListener(new ChainedRepositoryListener( - session.getRepositoryListener(), new ReverseTreeRepositoryListener())); - } - sessionBuilder.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true); - sessionBuilder.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true); - try (CloseableSession session = sessionBuilder.build()) { + try (CloseableSession session = Booter.newRepositorySystemSession(system) + .withRepositoryListener(new ReverseTreeRepositoryListener()) + .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true) + .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true) + .build()) { Artifact artifact = new DefaultArtifact("org.apache.maven:maven-resolver-provider:3.6.1"); ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/supplier/SupplierRepositorySystemFactory.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/supplier/SupplierRepositorySystemFactory.java index 3980a9fbd..bc69a8363 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/supplier/SupplierRepositorySystemFactory.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/supplier/SupplierRepositorySystemFactory.java @@ -35,7 +35,9 @@ public static RepositorySystem newRepositorySystem() { @Override protected Map createTransporterFactories() { Map result = super.createTransporterFactories(); - result.put(JdkTransporterFactory.NAME, new JdkTransporterFactory(getChecksumExtractor())); + result.put( + JdkTransporterFactory.NAME, + new JdkTransporterFactory(getChecksumExtractor(), getPathProcessor())); result.put(JettyTransporterFactory.NAME, new JettyTransporterFactory(getChecksumExtractor())); return result; } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/Booter.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/Booter.java index 4ec87581b..c93018550 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/Booter.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/Booter.java @@ -18,7 +18,7 @@ */ package org.apache.maven.resolver.examples.util; -import java.io.File; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -61,11 +61,22 @@ public static RepositorySystem newRepositorySystem(final String factory) { } public static SessionBuilder newRepositorySystemSession(RepositorySystem system) { - return new SessionBuilderSupplier(system) + // FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); + SessionBuilder result = new SessionBuilderSupplier(system) .get() - .withLocalRepositoryBaseDirectories(new File("target/local-repo")) + // .withLocalRepositoryBaseDirectories(fs.getPath("local-repo")) + .withLocalRepositoryBaseDirectories(Paths.get("target/local-repo")) .setRepositoryListener(new ConsoleRepositoryListener()) .setTransferListener(new ConsoleTransferListener()); + result.setConfigProperty("aether.syncContext.named.factory", "noop"); + // result.addOnSessionEndedHandler(() -> { + // try { + // fs.close(); + // } catch (IOException e) { + // throw new UncheckedIOException(e); + // } + // }); + return result; // uncomment to generate dirty trees // session.setDependencyGraphTransformer( null ); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/UpdateCheck.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/UpdateCheck.java index 2f991a712..2fa4635fa 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/UpdateCheck.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/UpdateCheck.java @@ -19,6 +19,7 @@ package org.eclipse.aether.impl; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.RepositoryException; import org.eclipse.aether.repository.RemoteRepository; @@ -37,7 +38,7 @@ public final class UpdateCheck { private T item; - private File file; + private Path path; private boolean fileValid = true; @@ -108,9 +109,21 @@ public UpdateCheck setItem(T item) { * Returns the local file of the item. * * @return The local file of the item. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Returns the local file of the item. + * + * @return The local file of the item. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -118,9 +131,22 @@ public File getFile() { * * @param file The file of the item, never {@code null} . * @return This object for chaining. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public UpdateCheck setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the local file of the item. + * + * @param path The file of the item, never {@code null} . + * @return This object for chaining. + * @since 2.0.0 + */ + public UpdateCheck setPath(Path path) { + this.path = path; return this; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java index f0980f68d..ca27ab8e9 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java @@ -22,8 +22,11 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -59,7 +62,7 @@ import org.eclipse.aether.spi.connector.ArtifactDownload; import org.eclipse.aether.spi.connector.RepositoryConnector; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.transfer.ArtifactFilteredOutException; @@ -110,7 +113,7 @@ public class DefaultArtifactResolver implements ArtifactResolver { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactResolver.class); - private final FileProcessor fileProcessor; + private final PathProcessor pathProcessor; private final RepositoryEventDispatcher repositoryEventDispatcher; @@ -133,7 +136,7 @@ public class DefaultArtifactResolver implements ArtifactResolver { @SuppressWarnings("checkstyle:parameternumber") @Inject public DefaultArtifactResolver( - FileProcessor fileProcessor, + PathProcessor pathProcessor, RepositoryEventDispatcher repositoryEventDispatcher, VersionResolver versionResolver, UpdateCheckManager updateCheckManager, @@ -143,7 +146,7 @@ public DefaultArtifactResolver( OfflineController offlineController, Map artifactResolverPostProcessors, RemoteRepositoryFilterManager remoteRepositoryFilterManager) { - this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); this.repositoryEventDispatcher = requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null"); @@ -227,13 +230,13 @@ private List resolve( String localPath = session.getSystemScopeHandler().getSystemPath(artifact); if (localPath != null) { // unhosted artifact, just validate file - File file = new File(localPath); - if (!file.isFile()) { + Path path = Paths.get(localPath); + if (!Files.isRegularFile(path)) { failures = true; result.addException( ArtifactResult.NO_REPOSITORY, new ArtifactNotFoundException(artifact, localPath)); } else { - artifact = artifact.setFile(file); + artifact = artifact.setPath(path); result.setArtifact(artifact); artifactResolved(session, trace, artifact, null, result.getExceptions()); } @@ -282,9 +285,9 @@ private List resolve( } if (workspace != null) { - File file = workspace.findArtifact(artifact); - if (file != null) { - artifact = artifact.setFile(file); + Path path = workspace.findArtifactPath(artifact); + if (path != null) { + artifact = artifact.setPath(path); result.setArtifact(artifact); result.setRepository(workspace.getRepository()); artifactResolved(session, trace, artifact, result.getRepository(), null); @@ -309,7 +312,7 @@ private List resolve( } try { - artifact = artifact.setFile(getFile(session, artifact, local.getFile())); + artifact = artifact.setPath(getPath(session, artifact, local.getPath())); result.setArtifact(artifact); artifactResolved(session, trace, artifact, result.getRepository(), null); } catch (ArtifactTransferException e) { @@ -328,7 +331,7 @@ private List resolve( continue; } - if (local.getFile() != null) { + if (local.getPath() != null) { LOGGER.info( "Artifact {} is present in the local repository, but cached from a remote repository ID that is unavailable in current build context, verifying that is downloadable from {}", artifact, @@ -393,7 +396,7 @@ private List resolve( ArtifactRequest request = result.getRequest(); Artifact artifact = result.getArtifact(); - if (artifact == null || artifact.getFile() == null) { + if (artifact == null || artifact.getPath() == null) { failures = true; if (result.getExceptions().isEmpty()) { Exception exception = @@ -420,7 +423,7 @@ private boolean isLocallyInstalled(LocalArtifactResult lar, VersionResult vr) { if (lar.isAvailable()) { return true; } - if (lar.getFile() != null) { + if (lar.getPath() != null) { // resolution of version range found locally installed artifact if (vr.getRepository() instanceof LocalRepository) { // resolution of (snapshot) version found locally installed artifact @@ -433,29 +436,31 @@ private boolean isLocallyInstalled(LocalArtifactResult lar, VersionResult vr) { return false; } - private File getFile(RepositorySystemSession session, Artifact artifact, File file) + private Path getPath(RepositorySystemSession session, Artifact artifact, Path path) throws ArtifactTransferException { if (artifact.isSnapshot() && !artifact.getVersion().equals(artifact.getBaseVersion()) && ConfigUtils.getBoolean( session, DEFAULT_SNAPSHOT_NORMALIZATION, CONFIG_PROP_SNAPSHOT_NORMALIZATION)) { - String name = file.getName().replace(artifact.getVersion(), artifact.getBaseVersion()); - File dst = new File(file.getParent(), name); - - boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified(); - if (copy) { - try { - fileProcessor.copy(file, dst); - dst.setLastModified(file.lastModified()); - } catch (IOException e) { - throw new ArtifactTransferException(artifact, null, e); + String name = path.getFileName().toString().replace(artifact.getVersion(), artifact.getBaseVersion()); + Path dst = path.getParent().resolve(name); + + try { + long pathLastModified = pathProcessor.lastModified(path, 0L); + boolean copy = pathProcessor.size(dst, 0L) != pathProcessor.size(path, 0L) + || pathProcessor.lastModified(dst, 0L) != pathLastModified; + if (copy) { + pathProcessor.copy(path, dst); + Files.setLastModifiedTime(dst, FileTime.fromMillis(pathLastModified)); } + } catch (IOException e) { + throw new ArtifactTransferException(artifact, null, e); } - file = dst; + path = dst; } - return file; + return path; } private void performDownloads(RepositorySystemSession session, ResolutionGroup group) { @@ -499,13 +504,13 @@ private List gatherDownloads(RepositorySystemSession session, download.setRequestContext(item.request.getRequestContext()); download.setListener(SafeTransferListener.wrap(session)); download.setTrace(item.trace); - if (item.local.getFile() != null) { - download.setFile(item.local.getFile()); + if (item.local.getPath() != null) { + download.setPath(item.local.getPath()); download.setExistenceCheck(true); } else { String path = lrm.getPathForRemoteArtifact(artifact, group.repository, item.request.getRequestContext()); - download.setFile(new File(lrm.getRepository().getBasedir(), path)); + download.setPath(lrm.getRepository().getBasePath().resolve(path)); } boolean snapshot = artifact.isSnapshot(); @@ -515,7 +520,7 @@ private List gatherDownloads(RepositorySystemSession session, if ((errorPolicy & ResolutionErrorPolicy.CACHE_ALL) != 0) { UpdateCheck check = new UpdateCheck<>(); check.setItem(artifact); - check.setFile(download.getFile()); + check.setPath(download.getPath()); check.setFileValid(false); check.setRepository(group.repository); check.setArtifactPolicy(policy.getArtifactUpdatePolicy()); @@ -551,7 +556,7 @@ private void evaluateDownloads(RepositorySystemSession session, ResolutionGroup item.resolved.set(true); item.result.setRepository(group.repository); try { - artifact = artifact.setFile(getFile(session, artifact, download.getFile())); + artifact = artifact.setPath(getPath(session, artifact, download.getPath())); item.result.setArtifact(artifact); lrm.add( @@ -601,7 +606,7 @@ private void artifactResolved( event.setRepository(repository); event.setExceptions(exceptions != null ? new ArrayList<>(exceptions) : null); if (artifact != null) { - event.setFile(artifact.getFile()); + event.setPath(artifact.getPath()); } repositoryEventDispatcher.dispatch(event.build()); @@ -629,7 +634,7 @@ private void artifactDownloaded( event.setRepository(repository); event.setException(exception); if (artifact != null) { - event.setFile(artifact.getFile()); + event.setPath(artifact.getPath()); } repositoryEventDispatcher.dispatch(event.build()); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultChecksumProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultChecksumProcessor.java new file mode 100644 index 000000000..cc1a3f69a --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultChecksumProcessor.java @@ -0,0 +1,88 @@ +/* + * 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.internal.impl; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; + +import static java.util.Objects.requireNonNull; + +/** + * A utility class helping with file-based operations. + */ +@Singleton +@Named +public class DefaultChecksumProcessor implements ChecksumProcessor { + private final PathProcessor pathProcessor; + + @Inject + public DefaultChecksumProcessor(PathProcessor pathProcessor) { + this.pathProcessor = requireNonNull(pathProcessor); + } + + @Override + public String readChecksum(final Path checksumPath) throws IOException { + // for now do exactly same as happened before, but FileProcessor is a component and can be replaced + String checksum = ""; + try (BufferedReader br = new BufferedReader( + new InputStreamReader(Files.newInputStream(checksumPath), StandardCharsets.UTF_8), 512)) { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if (!line.isEmpty()) { + checksum = line; + break; + } + } + } + + if (checksum.matches(".+= [0-9A-Fa-f]+")) { + int lastSpacePos = checksum.lastIndexOf(' '); + checksum = checksum.substring(lastSpacePos + 1); + } else { + int spacePos = checksum.indexOf(' '); + + if (spacePos != -1) { + checksum = checksum.substring(0, spacePos); + } + } + + return checksum; + } + + @Override + public void writeChecksum(Path target, String checksum) throws IOException { + // for now do exactly same as happened before, but FileProcessor is a component and can be replaced + pathProcessor.write(target, checksum); + } +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java index 20c13407f..ede10657c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java @@ -22,8 +22,9 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -60,7 +61,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.FileProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.transfer.ArtifactTransferException; import org.eclipse.aether.transfer.MetadataNotFoundException; @@ -77,7 +78,7 @@ @Singleton @Named public class DefaultDeployer implements Deployer { - private final FileProcessor fileProcessor; + private final PathProcessor pathProcessor; private final RepositoryEventDispatcher repositoryEventDispatcher; @@ -96,7 +97,7 @@ public class DefaultDeployer implements Deployer { @SuppressWarnings("checkstyle:parameternumber") @Inject public DefaultDeployer( - FileProcessor fileProcessor, + PathProcessor pathProcessor, RepositoryEventDispatcher repositoryEventDispatcher, RepositoryConnectorProvider repositoryConnectorProvider, RemoteRepositoryManager remoteRepositoryManager, @@ -104,7 +105,7 @@ public DefaultDeployer( Map metadataFactories, SyncContextFactory syncContextFactory, OfflineController offlineController) { - this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); this.repositoryEventDispatcher = requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); this.repositoryConnectorProvider = @@ -179,7 +180,7 @@ private DeployResult deploy(SyncContext syncContext, RepositorySystemSession ses iterator.set(artifact); - ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getFile()); + ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getPath()); upload.setTrace(trace); upload.setListener(new ArtifactUploadListener(catapult, upload)); artifactUploads.add(upload); @@ -257,9 +258,9 @@ private void upload( EventCatapult catapult) throws DeploymentException { LocalRepositoryManager lrm = session.getLocalRepositoryManager(); - File basedir = lrm.getRepository().getBasedir(); + Path basePath = lrm.getRepository().getBasePath(); - File dstFile = new File(basedir, lrm.getPathForRemoteMetadata(metadata, repository, "")); + Path dstPath = basePath.resolve(lrm.getPathForRemoteMetadata(metadata, repository, "")); if (metadata instanceof MergeableMetadata) { if (!((MergeableMetadata) metadata).isMerged()) { @@ -278,7 +279,7 @@ private void upload( RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); MetadataDownload download = new MetadataDownload(); download.setMetadata(metadata); - download.setFile(dstFile); + download.setPath(dstPath); download.setChecksumPolicy(policy.getChecksumPolicy()); download.setListener(SafeTransferListener.wrap(session)); download.setTrace(catapult.getTrace()); @@ -287,7 +288,12 @@ private void upload( Exception error = download.getException(); if (error instanceof MetadataNotFoundException) { - dstFile.delete(); + try { + Files.deleteIfExists(dstPath); + } catch (IOException e) { + throw new DeploymentException( + "Failed to delete cached metadata " + metadata + ": " + e.getMessage(), e); + } } event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); @@ -295,7 +301,7 @@ private void upload( event.setMetadata(metadata); event.setRepository(repository); event.setException(error); - event.setFile(dstFile); + event.setPath(dstPath); repositoryEventDispatcher.dispatch(event.build()); event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED); @@ -303,7 +309,7 @@ private void upload( event.setMetadata(metadata); event.setRepository(repository); event.setException(error); - event.setFile(dstFile); + event.setPath(dstPath); repositoryEventDispatcher.dispatch(event.build()); if (error != null && !(error instanceof MetadataNotFoundException)) { @@ -313,16 +319,16 @@ private void upload( } try { - ((MergeableMetadata) metadata).merge(dstFile, dstFile); + ((MergeableMetadata) metadata).merge(dstPath, dstPath); } catch (RepositoryException e) { throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); } } else { - if (metadata.getFile() == null) { + if (metadata.getPath() == null) { throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached."); } try { - fileProcessor.copy(metadata.getFile(), dstFile); + pathProcessor.copy(metadata.getPath(), dstPath); } catch (IOException e) { throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); } @@ -330,12 +336,12 @@ private void upload( UpdateCheck check = new UpdateCheck<>(); check.setItem(metadata); - check.setFile(dstFile); + check.setPath(dstPath); check.setRepository(repository); check.setAuthoritativeRepository(repository); updateCheckManager.touchMetadata(session, check); - MetadataUpload upload = new MetadataUpload(metadata, dstFile); + MetadataUpload upload = new MetadataUpload(metadata, dstPath); upload.setTrace(catapult.getTrace()); upload.setListener(new MetadataUploadListener(catapult, upload)); metadataUploads.add(upload); @@ -377,43 +383,43 @@ public RequestTrace getTrace() { return trace; } - public void artifactDeploying(Artifact artifact, File file) { + public void artifactDeploying(Artifact artifact, Path path) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING); event.setTrace(trace); event.setArtifact(artifact); event.setRepository(repository); - event.setFile(file); + event.setPath(path); dispatcher.dispatch(event.build()); } - public void artifactDeployed(Artifact artifact, File file, ArtifactTransferException exception) { + public void artifactDeployed(Artifact artifact, Path path, ArtifactTransferException exception) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED); event.setTrace(trace); event.setArtifact(artifact); event.setRepository(repository); - event.setFile(file); + event.setPath(path); event.setException(exception); dispatcher.dispatch(event.build()); } - public void metadataDeploying(Metadata metadata, File file) { + public void metadataDeploying(Metadata metadata, Path path) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING); event.setTrace(trace); event.setMetadata(metadata); event.setRepository(repository); - event.setFile(file); + event.setPath(path); dispatcher.dispatch(event.build()); } - public void metadataDeployed(Metadata metadata, File file, Exception exception) { + public void metadataDeployed(Metadata metadata, Path path, Exception exception) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED); event.setTrace(trace); event.setMetadata(metadata); event.setRepository(repository); - event.setFile(file); + event.setPath(path); event.setException(exception); dispatcher.dispatch(event.build()); @@ -436,21 +442,21 @@ static final class ArtifactUploadListener extends SafeTransferListener { public void transferInitiated(TransferEvent event) throws TransferCancelledException { super.transferInitiated(event); requireNonNull(event, "event cannot be null"); - catapult.artifactDeploying(transfer.getArtifact(), transfer.getFile()); + catapult.artifactDeploying(transfer.getArtifact(), transfer.getPath()); } @Override public void transferFailed(TransferEvent event) { super.transferFailed(event); requireNonNull(event, "event cannot be null"); - catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), transfer.getException()); + catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), transfer.getException()); } @Override public void transferSucceeded(TransferEvent event) { super.transferSucceeded(event); requireNonNull(event, "event cannot be null"); - catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), null); + catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), null); } } @@ -470,21 +476,21 @@ static final class MetadataUploadListener extends SafeTransferListener { public void transferInitiated(TransferEvent event) throws TransferCancelledException { super.transferInitiated(event); requireNonNull(event, "event cannot be null"); - catapult.metadataDeploying(transfer.getMetadata(), transfer.getFile()); + catapult.metadataDeploying(transfer.getMetadata(), transfer.getPath()); } @Override public void transferFailed(TransferEvent event) { super.transferFailed(event); requireNonNull(event, "event cannot be null"); - catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), transfer.getException()); + catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), transfer.getException()); } @Override public void transferSucceeded(TransferEvent event) { super.transferSucceeded(event); requireNonNull(event, "event cannot be null"); - catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), null); + catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), null); } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java index 12bd4797b..811960e5b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java @@ -32,7 +32,10 @@ /** * A utility class helping with file-based operations. + * + * @deprecated */ +@Deprecated @Singleton @Named public class DefaultFileProcessor implements FileProcessor { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java index f4c60bf3f..78b63b9ae 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java @@ -22,7 +22,8 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; @@ -48,7 +49,7 @@ import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalMetadataRegistration; import org.eclipse.aether.repository.LocalRepositoryManager; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import static java.util.Objects.requireNonNull; @@ -58,7 +59,7 @@ @Singleton @Named public class DefaultInstaller implements Installer { - private final FileProcessor fileProcessor; + private final PathProcessor pathProcessor; private final RepositoryEventDispatcher repositoryEventDispatcher; @@ -68,11 +69,11 @@ public class DefaultInstaller implements Installer { @Inject public DefaultInstaller( - FileProcessor fileProcessor, + PathProcessor pathProcessor, RepositoryEventDispatcher repositoryEventDispatcher, Map metadataFactories, SyncContextFactory syncContextFactory) { - this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); this.repositoryEventDispatcher = requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); this.metadataFactories = Collections.unmodifiableMap(metadataFactories); @@ -163,25 +164,25 @@ private List getMetadataGenerators( private void install(RepositorySystemSession session, RequestTrace trace, Artifact artifact) throws InstallationException { final LocalRepositoryManager lrm = session.getLocalRepositoryManager(); - final File srcFile = artifact.getFile(); - final File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact(artifact)); + final Path srcPath = artifact.getPath(); + final Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalArtifact(artifact)); - artifactInstalling(session, trace, artifact, dstFile); + artifactInstalling(session, trace, artifact, dstPath); Exception exception = null; try { - if (dstFile.equals(srcFile)) { - throw new IllegalStateException("cannot install " + dstFile + " to same path"); + if (dstPath.equals(srcPath)) { + throw new IllegalStateException("cannot install " + dstPath + " to same path"); } - fileProcessor.copy(srcFile, dstFile); - dstFile.setLastModified(srcFile.lastModified()); + pathProcessor.copy(srcPath, dstPath); + Files.setLastModifiedTime(dstPath, Files.getLastModifiedTime(srcPath)); lrm.add(session, new LocalArtifactRegistration(artifact)); } catch (Exception e) { exception = e; throw new InstallationException("Failed to install artifact " + artifact + ": " + e.getMessage(), e); } finally { - artifactInstalled(session, trace, artifact, dstFile, exception); + artifactInstalled(session, trace, artifact, dstPath, exception); } } @@ -189,19 +190,19 @@ private void install(RepositorySystemSession session, RequestTrace trace, Metada throws InstallationException { LocalRepositoryManager lrm = session.getLocalRepositoryManager(); - File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalMetadata(metadata)); + Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalMetadata(metadata)); - metadataInstalling(session, trace, metadata, dstFile); + metadataInstalling(session, trace, metadata, dstPath); Exception exception = null; try { if (metadata instanceof MergeableMetadata) { - ((MergeableMetadata) metadata).merge(dstFile, dstFile); + ((MergeableMetadata) metadata).merge(dstPath, dstPath); } else { - if (dstFile.equals(metadata.getFile())) { - throw new IllegalStateException("cannot install " + dstFile + " to same path"); + if (dstPath.equals(metadata.getPath())) { + throw new IllegalStateException("cannot install " + dstPath + " to same path"); } - fileProcessor.copy(metadata.getFile(), dstFile); + pathProcessor.copy(metadata.getPath(), dstPath); } lrm.add(session, new LocalMetadataRegistration(metadata)); @@ -209,51 +210,51 @@ private void install(RepositorySystemSession session, RequestTrace trace, Metada exception = e; throw new InstallationException("Failed to install metadata " + metadata + ": " + e.getMessage(), e); } finally { - metadataInstalled(session, trace, metadata, dstFile, exception); + metadataInstalled(session, trace, metadata, dstPath, exception); } } private void artifactInstalling( - RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile) { + RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLING); event.setTrace(trace); event.setArtifact(artifact); event.setRepository(session.getLocalRepositoryManager().getRepository()); - event.setFile(dstFile); + event.setPath(dstPath); repositoryEventDispatcher.dispatch(event.build()); } private void artifactInstalled( - RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile, Exception exception) { + RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath, Exception exception) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLED); event.setTrace(trace); event.setArtifact(artifact); event.setRepository(session.getLocalRepositoryManager().getRepository()); - event.setFile(dstFile); + event.setPath(dstPath); event.setException(exception); repositoryEventDispatcher.dispatch(event.build()); } private void metadataInstalling( - RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile) { + RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLING); event.setTrace(trace); event.setMetadata(metadata); event.setRepository(session.getLocalRepositoryManager().getRepository()); - event.setFile(dstFile); + event.setPath(dstPath); repositoryEventDispatcher.dispatch(event.build()); } private void metadataInstalled( - RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile, Exception exception) { + RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath, Exception exception) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLED); event.setTrace(trace); event.setMetadata(metadata); event.setRepository(session.getLocalRepositoryManager().getRepository()); - event.setFile(dstFile); + event.setPath(dstPath); event.setException(exception); repositoryEventDispatcher.dispatch(event.build()); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalRepositoryProvider.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalRepositoryProvider.java index a72c83d30..e2c23e658 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalRepositoryProvider.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalRepositoryProvider.java @@ -72,7 +72,7 @@ public LocalRepositoryManager newLocalRepositoryManager(RepositorySystemSession buffer.append("Using manager ").append(manager.getClass().getSimpleName()); Utils.appendClassLoader(buffer, manager); buffer.append(" with priority ").append(factory.getPriority()); - buffer.append(" for ").append(repository.getBasedir()); + buffer.append(" for ").append(repository.getBasePath()); LOGGER.debug(buffer.toString()); } @@ -93,7 +93,7 @@ public LocalRepositoryManager newLocalRepositoryManager(RepositorySystemSession if (factories.isEmpty()) { buffer.append("No local repository managers registered"); } else { - buffer.append("Cannot access ").append(repository.getBasedir()); + buffer.append("Cannot access ").append(repository.getBasePath()); buffer.append(" with type ").append(repository.getContentType()); buffer.append(" using the available factories "); factories.list(buffer); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java index 4bbe30cda..28a43ae17 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java @@ -22,7 +22,10 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -59,6 +62,7 @@ import org.eclipse.aether.spi.connector.MetadataDownload; import org.eclipse.aether.spi.connector.RepositoryConnector; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.transfer.MetadataNotFoundException; import org.eclipse.aether.transfer.MetadataTransferException; @@ -102,6 +106,9 @@ public class DefaultMetadataResolver implements MetadataResolver { private final RemoteRepositoryFilterManager remoteRepositoryFilterManager; + private final PathProcessor pathProcessor; + + @SuppressWarnings("checkstyle:parameternumber") @Inject public DefaultMetadataResolver( RepositoryEventDispatcher repositoryEventDispatcher, @@ -110,7 +117,8 @@ public DefaultMetadataResolver( RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory, OfflineController offlineController, - RemoteRepositoryFilterManager remoteRepositoryFilterManager) { + RemoteRepositoryFilterManager remoteRepositoryFilterManager, + PathProcessor pathProcessor) { this.repositoryEventDispatcher = requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null"); @@ -122,6 +130,7 @@ public DefaultMetadataResolver( this.offlineController = requireNonNull(offlineController, "offline controller cannot be null"); this.remoteRepositoryFilterManager = requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); } @Override @@ -154,7 +163,7 @@ private List resolve( final List results = new ArrayList<>(requests.size()); final List tasks = new ArrayList<>(requests.size()); - final Map localLastUpdates = new HashMap<>(); + final Map localLastUpdates = new HashMap<>(); final RemoteRepositoryFilter remoteRepositoryFilter = remoteRepositoryFilterManager.getRemoteRepositoryFilter(session); @@ -173,10 +182,10 @@ private List resolve( metadataResolving(session, trace, metadata, localRepo); - File localFile = getLocalFile(session, metadata); + Path localFile = getLocalFile(session, metadata); if (localFile != null) { - metadata = metadata.setFile(localFile); + metadata = metadata.setPath(localFile); result.setMetadata(metadata); } else { result.setException(new MetadataNotFoundException(metadata, localRepo)); @@ -209,13 +218,13 @@ private List resolve( new LocalMetadataRequest(metadata, repository, request.getRequestContext()); LocalMetadataResult lrmResult = lrm.find(session, localRequest); - File metadataFile = lrmResult.getFile(); + Path metadataPath = lrmResult.getPath(); try { Utils.checkOffline(session, offlineController, repository); } catch (RepositoryOfflineException e) { - if (metadataFile != null) { - metadata = metadata.setFile(metadataFile); + if (metadataPath != null) { + metadata = metadata.setPath(metadataPath); result.setMetadata(metadata); } else { String msg = "Cannot access " + repository.getId() + " (" + repository.getUrl() @@ -230,11 +239,11 @@ private List resolve( Long localLastUpdate = null; if (request.isFavorLocalRepository()) { - File localFile = getLocalFile(session, metadata); - localLastUpdate = localLastUpdates.get(localFile); + Path localPath = getLocalFile(session, metadata); + localLastUpdate = localLastUpdates.get(localPath); if (localLastUpdate == null) { - localLastUpdate = localFile != null ? localFile.lastModified() : 0; - localLastUpdates.put(localFile, localLastUpdate); + localLastUpdate = localPath != null ? pathProcessor.lastModified(localPath, 0L) : 0L; + localLastUpdates.put(localPath, localLastUpdate); } } @@ -248,11 +257,11 @@ private List resolve( check.setItem(metadata); // use 'main' installation file for the check (-> use requested repository) - File checkFile = new File( - session.getLocalRepository().getBasedir(), - session.getLocalRepositoryManager() + Path checkPath = session.getLocalRepository() + .getBasePath() + .resolve(session.getLocalRepositoryManager() .getPathForRemoteMetadata(metadata, repository, request.getRequestContext())); - check.setFile(checkFile); + check.setPath(checkPath); check.setRepository(repository); check.setAuthoritativeRepository(repo); check.setArtifactPolicy(policy.getArtifactUpdatePolicy()); @@ -274,19 +283,19 @@ private List resolve( RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); // install path may be different from lookup path - File installFile = new File( - session.getLocalRepository().getBasedir(), - session.getLocalRepositoryManager() + Path installPath = session.getLocalRepository() + .getBasePath() + .resolve(session.getLocalRepositoryManager() .getPathForRemoteMetadata( metadata, request.getRepository(), request.getRequestContext())); ResolveTask task = new ResolveTask( - session, trace, result, installFile, checks, policy.getChecksumPolicy()); + session, trace, result, installPath, checks, policy.getChecksumPolicy()); tasks.add(task); } else { result.setException(exception); - if (metadataFile != null) { - metadata = metadata.setFile(metadataFile); + if (metadataPath != null) { + metadata = metadata.setPath(metadataPath); result.setMetadata(metadata); } metadataResolved(session, trace, metadata, repository, result.getException()); @@ -330,7 +339,7 @@ private List resolve( task.trace, task.request.getMetadata(), task.request.getRepository(), - task.metadataFile, + task.metadataPath, task.exception); task.result.setException(task.exception); @@ -343,11 +352,11 @@ private List resolve( // re-lookup metadata for resolve LocalMetadataRequest localRequest = new LocalMetadataRequest( metadata, task.request.getRepository(), task.request.getRequestContext()); - File metadataFile = session.getLocalRepositoryManager() + Path metadataPath = session.getLocalRepositoryManager() .find(session, localRequest) - .getFile(); - if (metadataFile != null) { - metadata = metadata.setFile(metadataFile); + .getPath(); + if (metadataPath != null) { + metadata = metadata.setPath(metadataPath); task.result.setMetadata(metadata); } if (task.result.getException() == null) { @@ -369,10 +378,10 @@ private List resolve( } } - private File getLocalFile(RepositorySystemSession session, Metadata metadata) { + private Path getLocalFile(RepositorySystemSession session, Metadata metadata) { LocalRepositoryManager lrm = session.getLocalRepositoryManager(); LocalMetadataResult localResult = lrm.find(session, new LocalMetadataRequest(metadata, null, null)); - return localResult.getFile(); + return localResult.getPath(); } private List getEnabledSourceRepositories(RemoteRepository repository, Metadata.Nature nature) { @@ -428,7 +437,7 @@ private void metadataResolved( event.setMetadata(metadata); event.setRepository(repository); event.setException(exception); - event.setFile(metadata.getFile()); + event.setPath(metadata.getPath()); repositoryEventDispatcher.dispatch(event.build()); } @@ -448,14 +457,14 @@ private void metadataDownloaded( RequestTrace trace, Metadata metadata, ArtifactRepository repository, - File file, + Path path, Exception exception) { RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); event.setTrace(trace); event.setMetadata(metadata); event.setRepository(repository); event.setException(exception); - event.setFile(file); + event.setPath(path); repositoryEventDispatcher.dispatch(event.build()); } @@ -469,7 +478,7 @@ class ResolveTask implements Runnable { final MetadataRequest request; - final File metadataFile; + final Path metadataPath; final String policy; @@ -481,14 +490,14 @@ class ResolveTask implements Runnable { RepositorySystemSession session, RequestTrace trace, MetadataResult result, - File metadataFile, + Path metadataPath, List> checks, String policy) { this.session = session; this.trace = trace; this.result = result; this.request = result.getRequest(); - this.metadataFile = metadataFile; + this.metadataPath = metadataPath; this.policy = policy; this.checks = checks; } @@ -506,7 +515,7 @@ public void run() { MetadataDownload download = new MetadataDownload(); download.setMetadata(metadata); download.setRequestContext(request.getRequestContext()); - download.setFile(metadataFile); + download.setPath(metadataPath); download.setChecksumPolicy(policy); download.setRepositories(repositories); download.setListener(SafeTransferListener.wrap(session)); @@ -527,7 +536,11 @@ public void run() { session.getLocalRepositoryManager().add(session, registration); } else if (request.isDeleteLocalCopyIfMissing() && exception instanceof MetadataNotFoundException) { - download.getFile().delete(); + try { + Files.deleteIfExists(download.getPath()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } } catch (NoRepositoryConnectorException e) { exception = new MetadataTransferException(metadata, requestRepository, e); 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 new file mode 100644 index 000000000..b3af35bb0 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultPathProcessor.java @@ -0,0 +1,95 @@ +/* + * 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.internal.impl; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.util.FileUtils; + +/** + * A utility class helping with file-based operations. + */ +@Singleton +@Named +public class DefaultPathProcessor implements PathProcessor { + @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 void copy(Path source, Path target) throws IOException { + copy(source, target, null); + } + + @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); + } +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java index 8bef789a2..a4e731300 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java @@ -21,17 +21,18 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.RandomAccessFile; import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.Map; import java.util.Properties; @@ -51,17 +52,26 @@ public final class DefaultTrackingFileManager implements TrackingFileManager { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTrackingFileManager.class); + @Deprecated @Override public Properties read(File file) { - if (Files.isReadable(file.toPath())) { - synchronized (getMutex(file)) { - try (FileInputStream stream = new FileInputStream(file); - FileLock unused = fileLock(stream.getChannel(), Math.max(1, file.length()), true)) { - Properties props = new Properties(); - props.load(stream); - return props; + return read(file.toPath()); + } + + @Override + public Properties read(Path path) { + if (Files.isReadable(path)) { + synchronized (getMutex(path)) { + try { + long fileSize = Files.size(path); + try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ); + FileLock unused = fileLock(fileChannel, Math.max(1, fileSize), true)) { + Properties props = new Properties(); + props.load(Channels.newInputStream(fileChannel)); + return props; + } } catch (IOException e) { - LOGGER.warn("Failed to read tracking file '{}'", file, e); + LOGGER.warn("Failed to read tracking file '{}'", path, e); throw new UncheckedIOException(e); } } @@ -69,45 +79,57 @@ public Properties read(File file) { return null; } + @Deprecated @Override public Properties update(File file, Map updates) { - Properties props = new Properties(); + return update(file.toPath(), updates); + } + @Override + public Properties update(Path path, Map updates) { + Properties props = new Properties(); try { - Files.createDirectories(file.getParentFile().toPath()); + Files.createDirectories(path.getParent()); } catch (IOException e) { - LOGGER.warn("Failed to create tracking file parent '{}'", file, e); + LOGGER.warn("Failed to create tracking file parent '{}'", path, e); throw new UncheckedIOException(e); } - synchronized (getMutex(file)) { - try (RandomAccessFile raf = new RandomAccessFile(file, "rw"); - FileLock unused = fileLock(raf.getChannel(), Math.max(1, raf.length()), false)) { - if (raf.length() > 0) { - byte[] buffer = new byte[(int) raf.length()]; - raf.readFully(buffer); - props.load(new ByteArrayInputStream(buffer)); + synchronized (getMutex(path)) { + try { + long fileSize; + try { + fileSize = Files.size(path); + } catch (IOException e) { + fileSize = 0L; } + try (FileChannel fileChannel = FileChannel.open( + path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); + FileLock unused = fileLock(fileChannel, Math.max(1, fileSize), false)) { + if (fileSize > 0) { + props.load(Channels.newInputStream(fileChannel)); + } - for (Map.Entry update : updates.entrySet()) { - if (update.getValue() == null) { - props.remove(update.getKey()); - } else { - props.setProperty(update.getKey(), update.getValue()); + for (Map.Entry update : updates.entrySet()) { + if (update.getValue() == null) { + props.remove(update.getKey()); + } else { + props.setProperty(update.getKey(), update.getValue()); + } } - } - LOGGER.debug("Writing tracking file '{}'", file); - ByteArrayOutputStream stream = new ByteArrayOutputStream(1024 * 2); - props.store( - stream, - "NOTE: This is a Maven Resolver internal implementation file" - + ", its format can be changed without prior notice."); - raf.seek(0L); - raf.write(stream.toByteArray()); - raf.setLength(raf.getFilePointer()); + LOGGER.debug("Writing tracking file '{}'", path); + ByteArrayOutputStream stream = new ByteArrayOutputStream(1024 * 2); + props.store( + stream, + "NOTE: This is a Maven Resolver internal implementation file" + + ", its format can be changed without prior notice."); + fileChannel.position(0); + int written = fileChannel.write(ByteBuffer.wrap(stream.toByteArray())); + fileChannel.truncate(written); + } } catch (IOException e) { - LOGGER.warn("Failed to write tracking file '{}'", file, e); + LOGGER.warn("Failed to write tracking file '{}'", path, e); throw new UncheckedIOException(e); } } @@ -115,7 +137,7 @@ public Properties update(File file, Map updates) { return props; } - private Object getMutex(File file) { + private Object getMutex(Path path) { // The interned string of path is (mis)used as mutex, to exclude different threads going for same file, // as JVM file locking happens on JVM not on Thread level. This is how original code did it ¯\_(ツ)_/¯ /* @@ -123,13 +145,7 @@ private Object getMutex(File file) { * piece of code might have locked the same file (unlikely though) or the canonical path fails to capture file * identity sufficiently as is the case with Java 1.6 and symlinks on Windows. */ - try { - return file.getCanonicalPath().intern(); - } catch (IOException e) { - LOGGER.warn("Failed to canonicalize path {}", file, e); - // TODO This is code smell and deprecated - return file.getAbsolutePath().intern(); - } + return path.toAbsolutePath().normalize().toString().intern(); } @SuppressWarnings({"checkstyle:magicnumber"}) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java index 51c03ce49..2c5e1965e 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java @@ -22,7 +22,10 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -43,6 +46,7 @@ import org.eclipse.aether.repository.Proxy; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ResolutionErrorPolicy; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.ArtifactNotFoundException; import org.eclipse.aether.transfer.ArtifactTransferException; import org.eclipse.aether.transfer.MetadataNotFoundException; @@ -118,11 +122,16 @@ public String toString() { private final UpdatePolicyAnalyzer updatePolicyAnalyzer; + private final PathProcessor pathProcessor; + @Inject public DefaultUpdateCheckManager( - TrackingFileManager trackingFileManager, UpdatePolicyAnalyzer updatePolicyAnalyzer) { + TrackingFileManager trackingFileManager, + UpdatePolicyAnalyzer updatePolicyAnalyzer, + PathProcessor pathProcessor) { this.trackingFileManager = requireNonNull(trackingFileManager, "tracking file manager cannot be null"); this.updatePolicyAnalyzer = requireNonNull(updatePolicyAnalyzer, "update policy analyzer cannot be null"); + this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); } @Override @@ -141,15 +150,15 @@ public void checkArtifact(RepositorySystemSession session, UpdateCheck check) { requireNonNull(session, "session cannot be null"); requireNonNull(check, "check cannot be null"); - File artifactFile = check.getFile(); - File touchFile = getArtifactTouchFile(artifactFile); + Path artifactPath = check.getPath(); + Path touchPath = getArtifactTouchFile(artifactPath); - String updateKey = getUpdateKey(session, artifactFile, check.getRepository()); + String updateKey = getUpdateKey(session, artifactPath, check.getRepository()); String dataKey = getDataKey(check.getAuthoritativeRepository()); String transferKey = getTransferKey(session, check.getRepository()); setUpdated(session, updateKey); - Properties props = write(touchFile, dataKey, transferKey, check.getException()); + Properties props = write(touchPath, dataKey, transferKey, check.getException()); - if (artifactFile.exists() && !hasErrors(props)) { - touchFile.delete(); + if (Files.exists(artifactPath) && !hasErrors(props)) { + try { + Files.deleteIfExists(touchPath); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } } @@ -490,18 +503,18 @@ private boolean hasErrors(Properties props) { public void touchMetadata(RepositorySystemSession session, UpdateCheck check) { requireNonNull(session, "session cannot be null"); requireNonNull(check, "check cannot be null"); - File metadataFile = check.getFile(); - File touchFile = getMetadataTouchFile(metadataFile); + Path metadataPath = check.getPath(); + Path touchPath = getMetadataTouchFile(metadataPath); - String updateKey = getUpdateKey(session, metadataFile, check.getRepository()); - String dataKey = getDataKey(metadataFile); - String transferKey = getTransferKey(session, metadataFile, check.getRepository()); + String updateKey = getUpdateKey(session, metadataPath, check.getRepository()); + String dataKey = getDataKey(metadataPath); + String transferKey = getTransferKey(session, metadataPath, check.getRepository()); setUpdated(session, updateKey); - write(touchFile, dataKey, transferKey, check.getException()); + write(touchPath, dataKey, transferKey, check.getException()); } - private Properties write(File touchFile, String dataKey, String transferKey, Exception error) { + private Properties write(Path touchPath, String dataKey, String transferKey, Exception error) { Map updates = new HashMap<>(); String timestamp = Long.toString(System.currentTimeMillis()); @@ -524,6 +537,6 @@ private Properties write(File touchFile, String dataKey, String transferKey, Exc updates.put(transferKey + UPDATED_KEY_SUFFIX, timestamp); } - return trackingFileManager.update(touchFile, updates); + return trackingFileManager.update(touchPath, updates); } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java index 8191f0b09..a414c8588 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java @@ -18,7 +18,8 @@ */ package org.eclipse.aether.internal.impl; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -67,7 +68,7 @@ class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager { private final LocalPathPrefixComposer localPathPrefixComposer; EnhancedLocalRepositoryManager( - File basedir, + Path basedir, LocalPathComposer localPathComposer, String trackingFilename, TrackingFileManager trackingFileManager, @@ -119,21 +120,21 @@ public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRe LocalArtifactResult result = new LocalArtifactResult(request); String path; - File file; + Path filePath; // Local repository CANNOT have timestamped installed, they are created only during deploy if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) { path = getPathForLocalArtifact(artifact); - file = new File(getRepository().getBasedir(), path); - checkFind(file, result); + filePath = getRepository().getBasePath().resolve(path); + checkFind(filePath, result); } if (!result.isAvailable()) { for (RemoteRepository repository : request.getRepositories()) { path = getPathForRemoteArtifact(artifact, repository, request.getContext()); - file = new File(getRepository().getBasedir(), path); + filePath = getRepository().getBasePath().resolve(path); - checkFind(file, result); + checkFind(filePath, result); if (result.isAvailable()) { break; @@ -144,19 +145,19 @@ public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRe return result; } - private void checkFind(File file, LocalArtifactResult result) { - if (file.isFile()) { - result.setFile(file); + private void checkFind(Path path, LocalArtifactResult result) { + if (Files.isRegularFile(path)) { + result.setPath(path); - Properties props = readRepos(file); + Properties props = readRepos(path); - if (props.get(getKey(file, LOCAL_REPO_ID)) != null) { + if (props.get(getKey(path, LOCAL_REPO_ID)) != null) { // artifact installed into the local repo is always accepted result.setAvailable(true); } else { String context = result.getRequest().getContext(); for (RemoteRepository repository : result.getRequest().getRepositories()) { - if (props.get(getKey(file, getRepositoryKey(repository, context))) != null) { + if (props.get(getKey(path, getRepositoryKey(repository, context))) != null) { // artifact downloaded from remote repository is accepted only downloaded from request // repositories result.setAvailable(true); @@ -164,7 +165,7 @@ private void checkFind(File file, LocalArtifactResult result) { break; } } - if (!result.isAvailable() && !isTracked(props, file)) { + if (!result.isAvailable() && !isTracked(props, path)) { /* * NOTE: The artifact is present but not tracked at all, for inter-op with simple local repo, assume * the artifact was locally installed. @@ -210,40 +211,40 @@ private void addArtifact( String path = repository == null ? getPathForLocalArtifact(artifact) : getPathForRemoteArtifact(artifact, repository, context); - File file = new File(getRepository().getBasedir(), path); + Path file = getRepository().getBasePath().resolve(path); addRepo(file, repositories); } - private Properties readRepos(File artifactFile) { - File trackingFile = getTrackingFile(artifactFile); + private Properties readRepos(Path artifactPath) { + Path trackingFile = getTrackingFile(artifactPath); Properties props = trackingFileManager.read(trackingFile); return (props != null) ? props : new Properties(); } - private void addRepo(File artifactFile, Collection repositories) { + private void addRepo(Path artifactPath, Collection repositories) { Map updates = new HashMap<>(); for (String repository : repositories) { - updates.put(getKey(artifactFile, repository), ""); + updates.put(getKey(artifactPath, repository), ""); } - File trackingFile = getTrackingFile(artifactFile); + Path trackingPath = getTrackingFile(artifactPath); - trackingFileManager.update(trackingFile, updates); + trackingFileManager.update(trackingPath, updates); } - private File getTrackingFile(File artifactFile) { - return new File(artifactFile.getParentFile(), trackingFilename); + private Path getTrackingFile(Path artifactPath) { + return artifactPath.getParent().resolve(trackingFilename); } - private String getKey(File file, String repository) { - return file.getName() + '>' + repository; + private String getKey(Path path, String repository) { + return path.getFileName() + ">" + repository; } - private boolean isTracked(Properties props, File file) { + private boolean isTracked(Properties props, Path path) { if (props != null) { - String keyPrefix = file.getName() + '>'; + String keyPrefix = path.getFileName() + ">"; for (Object key : props.keySet()) { if (key.toString().startsWith(keyPrefix)) { return true; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index d25ea85aa..561540d19 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -91,7 +91,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local if ("".equals(repository.getContentType()) || "default".equals(repository.getContentType())) { return new EnhancedLocalRepositoryManager( - repository.getBasedir(), + repository.getBasePath(), localPathComposer, trackingFilename, trackingFileManager, diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index 82f6ddfe2..ee393140d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -18,7 +18,8 @@ */ package org.eclipse.aether.internal.impl; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; @@ -48,9 +49,9 @@ class SimpleLocalRepositoryManager implements LocalRepositoryManager { private final LocalPathComposer localPathComposer; - SimpleLocalRepositoryManager(File basedir, String type, LocalPathComposer localPathComposer) { - requireNonNull(basedir, "base directory cannot be null"); - repository = new LocalRepository(basedir.getAbsoluteFile(), type); + SimpleLocalRepositoryManager(Path basePath, String type, LocalPathComposer localPathComposer) { + requireNonNull(basePath, "base directory cannot be null"); + repository = new LocalRepository(basePath.toAbsolutePath(), type); this.localPathComposer = requireNonNull(localPathComposer); } @@ -132,14 +133,14 @@ public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRe LocalArtifactResult result = new LocalArtifactResult(request); String path; - File file; + Path filePath; // Local repository CANNOT have timestamped installed, they are created only during deploy if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) { path = getPathForLocalArtifact(artifact); - file = new File(getRepository().getBasedir(), path); - if (file.isFile()) { - result.setFile(file); + filePath = getRepository().getBasePath().resolve(path); + if (Files.isRegularFile(filePath)) { + result.setPath(filePath); result.setAvailable(true); } } @@ -147,9 +148,9 @@ public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRe if (!result.isAvailable()) { for (RemoteRepository repository : request.getRepositories()) { path = getPathForRemoteArtifact(artifact, repository, request.getContext()); - file = new File(getRepository().getBasedir(), path); - if (file.isFile()) { - result.setFile(file); + filePath = getRepository().getBasePath().resolve(path); + if (Files.isRegularFile(filePath)) { + result.setPath(filePath); result.setAvailable(true); break; } @@ -184,9 +185,9 @@ public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRe path = getPathForLocalMetadata(metadata); } - File file = new File(getRepository().getBasedir(), path); - if (file.isFile()) { - result.setFile(file); + Path filePath = getRepository().getBasePath().resolve(path); + if (Files.isRegularFile(filePath)) { + result.setPath(filePath); } return result; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index 55ddb1e19..f4a3cc7ff 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -60,7 +60,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local requireNonNull(repository, "repository cannot be null"); if ("".equals(repository.getContentType()) || "simple".equals(repository.getContentType())) { - return new SimpleLocalRepositoryManager(repository.getBasedir(), "simple", localPathComposer); + return new SimpleLocalRepositoryManager(repository.getBasePath(), "simple", localPathComposer); } else { throw new NoLocalRepositoryManagerException(repository); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java index 5e922bf58..d7a03ab87 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java @@ -19,6 +19,7 @@ package org.eclipse.aether.internal.impl; import java.io.File; +import java.nio.file.Path; import java.util.Map; import java.util.Properties; @@ -28,12 +29,29 @@ public interface TrackingFileManager { /** * Reads up the specified properties file into {@link Properties}, if exists, otherwise {@code null} is returned. + * @deprecated Use {@link #read(Path)} instead. */ + @Deprecated Properties read(File file); + /** + * Reads up the specified properties file into {@link Properties}, if exists, otherwise {@code null} is returned. + * @since 2.0.0 + */ + Properties read(Path path); + /** * Applies updates to specified properties file and returns resulting {@link Properties} with contents same * as in updated file, never {@code null}. + * @deprecated Use {@link #update(Path, Map)} instead. */ + @Deprecated Properties update(File file, Map updates); + + /** + * Applies updates to specified properties file and returns resulting {@link Properties} with contents same + * as in updated file, never {@code null}. + * @since 2.0.0 + */ + Properties update(Path path, Map updates); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 66eb0dd9e..5a4013dfc 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -35,7 +35,7 @@ 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.FileProcessor; +import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.util.ConfigUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,13 +98,14 @@ public final class SparseDirectoryTrustedChecksumsSource extends FileTrustedChec private static final Logger LOGGER = LoggerFactory.getLogger(SparseDirectoryTrustedChecksumsSource.class); - private final FileProcessor fileProcessor; + private final ChecksumProcessor checksumProcessor; private final LocalPathComposer localPathComposer; @Inject - public SparseDirectoryTrustedChecksumsSource(FileProcessor fileProcessor, LocalPathComposer localPathComposer) { - this.fileProcessor = requireNonNull(fileProcessor); + public SparseDirectoryTrustedChecksumsSource( + ChecksumProcessor checksumProcessor, LocalPathComposer localPathComposer) { + this.checksumProcessor = requireNonNull(checksumProcessor); this.localPathComposer = requireNonNull(localPathComposer); } @@ -141,7 +142,7 @@ protected Map doGetTrustedArtifactChecksums( } try { - String checksum = fileProcessor.readChecksum(checksumPath.toFile()); + String checksum = checksumProcessor.readChecksum(checksumPath); if (checksum != null) { checksums.put(checksumAlgorithmFactory.getName(), checksum); } @@ -196,7 +197,7 @@ public void addTrustedArtifactChecksums( Path checksumPath = basedir.resolve( calculateArtifactPath(originAware, artifact, artifactRepository, checksumAlgorithmFactory)); String checksum = requireNonNull(trustedArtifactChecksums.get(checksumAlgorithmFactory.getName())); - fileProcessor.writeChecksum(checksumPath.toFile(), checksum); + checksumProcessor.writeChecksum(checksumPath, checksum); } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java index 709ac446a..95e594850 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java @@ -169,7 +169,7 @@ final boolean record = ConfigUtils.getBoolean(session, false, CONFIG_PROP_RECORD if (record) { recordArtifactChecksums(session, artifactResult, checksumAlgorithms); } else if (!validateArtifactChecksums(session, artifactResult, checksumAlgorithms, failIfMissing)) { - artifactResult.setArtifact(artifactResult.getArtifact().setFile(null)); // make it unresolved + artifactResult.setArtifact(artifactResult.getArtifact().setPath(null)); // make it unresolved } } } @@ -186,7 +186,7 @@ private void recordArtifactChecksums( ArtifactRepository artifactRepository = artifactResult.getRepository(); try { final Map calculatedChecksums = - ChecksumAlgorithmHelper.calculate(artifact.getFile(), checksumAlgorithmFactories); + ChecksumAlgorithmHelper.calculate(artifact.getPath(), checksumAlgorithmFactories); for (TrustedChecksumsSource trustedChecksumsSource : trustedChecksumsSources.values()) { TrustedChecksumsSource.Writer writer = @@ -197,12 +197,12 @@ private void recordArtifactChecksums( artifact, artifactRepository, checksumAlgorithmFactories, calculatedChecksums); } catch (IOException e) { throw new UncheckedIOException( - "Could not write required checksums for " + artifact.getFile(), e); + "Could not write required checksums for " + artifact.getPath(), e); } } } } catch (IOException e) { - throw new UncheckedIOException("Could not calculate required checksums for " + artifact.getFile(), e); + throw new UncheckedIOException("Could not calculate required checksums for " + artifact.getPath(), e); } } @@ -222,7 +222,7 @@ private boolean validateArtifactChecksums( try { // full set: calculate all algorithms we were asked for final Map calculatedChecksums = - ChecksumAlgorithmHelper.calculate(artifact.getFile(), checksumAlgorithmFactories); + ChecksumAlgorithmHelper.calculate(artifact.getPath(), checksumAlgorithmFactories); for (Map.Entry entry : trustedChecksumsSources.entrySet()) { final String trustedSourceName = entry.getKey(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultCloseableSession.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultCloseableSession.java index 4aca985d0..dc8086902 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultCloseableSession.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultCloseableSession.java @@ -18,9 +18,7 @@ */ package org.eclipse.aether.internal.impl.session; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.aether.*; @@ -41,6 +39,8 @@ import org.eclipse.aether.resolution.ArtifactDescriptorPolicy; import org.eclipse.aether.resolution.ResolutionErrorPolicy; import org.eclipse.aether.transfer.TransferListener; +import org.eclipse.aether.util.listener.ChainedRepositoryListener; +import org.eclipse.aether.util.listener.ChainedTransferListener; import static java.util.Objects.requireNonNull; @@ -119,10 +119,10 @@ public DefaultCloseableSession( String artifactUpdatePolicy, String metadataUpdatePolicy, LocalRepositoryManager localRepositoryManager, - List localRepositories, + Collection localRepositories, WorkspaceReader workspaceReader, - RepositoryListener repositoryListener, - TransferListener transferListener, + Collection repositoryListener, + Collection transferListener, Map systemProperties, Map userProperties, Map configProperties, @@ -138,6 +138,7 @@ public DefaultCloseableSession( SessionData data, RepositoryCache cache, SystemScopeHandler systemScopeHandler, + List onSessionEndedHandlers, RepositorySystem repositorySystem, RepositorySystemLifecycle repositorySystemLifecycle) { this.sessionId = requireNonNull(sessionId); @@ -150,8 +151,8 @@ public DefaultCloseableSession( this.artifactUpdatePolicy = artifactUpdatePolicy; this.metadataUpdatePolicy = metadataUpdatePolicy; this.workspaceReader = workspaceReader; - this.repositoryListener = repositoryListener; - this.transferListener = transferListener; + this.repositoryListener = new ChainedRepositoryListener(repositoryListener); + this.transferListener = new ChainedTransferListener(transferListener); this.systemProperties = Collections.unmodifiableMap(systemProperties); this.userProperties = Collections.unmodifiableMap(userProperties); this.configProperties = Collections.unmodifiableMap(configProperties); @@ -174,14 +175,15 @@ public DefaultCloseableSession( this.localRepositoryManager = getOrCreateLocalRepositoryManager(localRepositoryManager, localRepositories); repositorySystemLifecycle.sessionStarted(this); + onSessionEndedHandlers.forEach(this::addOnSessionEndedHandler); } private LocalRepositoryManager getOrCreateLocalRepositoryManager( - LocalRepositoryManager localRepositoryManager, List localRepositories) { + LocalRepositoryManager localRepositoryManager, Collection localRepositories) { if (localRepositoryManager != null) { return localRepositoryManager; } else if (localRepositories != null) { - return repositorySystem.newLocalRepositoryManager(this, localRepositories); + return repositorySystem.newLocalRepositoryManager(this, new ArrayList<>(localRepositories)); } else { throw new IllegalStateException("No local repository manager or local repositories set on session"); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultSessionBuilder.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultSessionBuilder.java index 845a2f391..630fdf9e5 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultSessionBuilder.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/session/DefaultSessionBuilder.java @@ -18,16 +18,19 @@ */ package org.eclipse.aether.internal.impl.session; -import java.io.File; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.nio.file.Path; +import java.util.*; import java.util.function.Supplier; -import org.eclipse.aether.*; +import org.eclipse.aether.DefaultSessionData; +import org.eclipse.aether.RepositoryCache; +import org.eclipse.aether.RepositoryListener; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession.CloseableSession; import org.eclipse.aether.RepositorySystemSession.SessionBuilder; +import org.eclipse.aether.SessionData; +import org.eclipse.aether.SystemScopeHandler; import org.eclipse.aether.artifact.ArtifactTypeRegistry; import org.eclipse.aether.collection.DependencyGraphTransformer; import org.eclipse.aether.collection.DependencyManager; @@ -87,13 +90,13 @@ public final class DefaultSessionBuilder implements SessionBuilder { private LocalRepositoryManager localRepositoryManager; - private List localRepositories; + private Collection localRepositories; private WorkspaceReader workspaceReader; - private RepositoryListener repositoryListener; + private final ArrayList repositoryListener = new ArrayList<>(); - private TransferListener transferListener; + private final ArrayList transferListener = new ArrayList<>(); private Map systemProperties = new HashMap<>(); @@ -125,6 +128,8 @@ public final class DefaultSessionBuilder implements SessionBuilder { private SystemScopeHandler systemScopeHandler = SystemScopeHandler.LEGACY; + private final ArrayList onSessionCloseHandlers = new ArrayList<>(); + /** * Constructor for "top level" builders. */ @@ -200,13 +205,19 @@ public DefaultSessionBuilder setWorkspaceReader(WorkspaceReader workspaceReader) @Override public DefaultSessionBuilder setRepositoryListener(RepositoryListener repositoryListener) { - this.repositoryListener = repositoryListener; + this.repositoryListener.clear(); + if (repositoryListener != null) { + this.repositoryListener.add(repositoryListener); + } return this; } @Override public DefaultSessionBuilder setTransferListener(TransferListener transferListener) { - this.transferListener = transferListener; + this.transferListener.clear(); + if (transferListener != null) { + this.transferListener.add(transferListener); + } return this; } @@ -348,6 +359,13 @@ public DefaultSessionBuilder setSystemScopeHandler(SystemScopeHandler systemScop return this; } + @Override + public SessionBuilder addOnSessionEndedHandler(Runnable handler) { + requireNonNull(handler, "null handler"); + onSessionCloseHandlers.add(handler); + return this; + } + @Override public DefaultSessionBuilder setRepositoryCacheSupplier(Supplier cacheSupplier) { requireNonNull(cacheSupplier, "null cacheSupplier"); @@ -356,12 +374,12 @@ public DefaultSessionBuilder setRepositoryCacheSupplier(Supplier baseDirectories) { + public SessionBuilder withLocalRepositoryBaseDirectories(Collection baseDirectories) { requireNonNull(baseDirectories, "null baseDirectories"); return withLocalRepositories( baseDirectories.stream().map(LocalRepository::new).collect(toList())); @@ -373,12 +391,34 @@ public SessionBuilder withLocalRepositories(LocalRepository... localRepositories } @Override - public SessionBuilder withLocalRepositories(List localRepositories) { + public SessionBuilder withLocalRepositories(Collection localRepositories) { requireNonNull(localRepositories, "null localRepositories"); this.localRepositories = localRepositories; return this; } + @Override + public SessionBuilder withRepositoryListener(RepositoryListener... repositoryListeners) { + return withRepositoryListener(Arrays.asList(repositoryListeners)); + } + + @Override + public SessionBuilder withRepositoryListener(Collection repositoryListeners) { + this.repositoryListener.addAll(repositoryListeners); + return this; + } + + @Override + public SessionBuilder withTransferListener(TransferListener... transferListeners) { + return withTransferListener(Arrays.asList(transferListeners)); + } + + @Override + public SessionBuilder withTransferListener(Collection transferListeners) { + this.transferListener.addAll(transferListeners); + return this; + } + @Override public SessionBuilder withRepositorySystemSession(RepositorySystemSession session) { requireNonNull(session, "repository system session cannot be null"); @@ -442,6 +482,7 @@ public CloseableSession build() { sessionDataSupplier.get(), repositoryCacheSupplier.get(), systemScopeHandler, + onSessionCloseHandlers, repositorySystem, repositorySystemLifecycle); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapper.java index 8ae07dc2e..c3b192a07 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapper.java @@ -52,13 +52,33 @@ public class BasedirNameMapper implements NameMapper { private final NameMapper delegate; + private final Path basePath; + public BasedirNameMapper(final NameMapper delegate) { - this.delegate = requireNonNull(delegate); + this(delegate, null); + } + + /** + * Creates basedir name mapper with provided path as base. + * + * @param delegate The delegate to resolve against basedir, must not be {@code null}. The delegate must be + * "file system friendly", see {@link NameMapper#isFileSystemFriendly()} method. + * @param path The basedir, may be {@code null} in which case given session local repository root is used as + * basedir. + * @since 2.0.0 + */ + public BasedirNameMapper(final NameMapper delegate, final Path path) { + requireNonNull(delegate); + if (!delegate.isFileSystemFriendly()) { + throw new IllegalArgumentException("delegate must be file-system friendly"); + } + this.delegate = delegate; + this.basePath = path; } @Override public boolean isFileSystemFriendly() { - return delegate.isFileSystemFriendly(); + return true; // it enforces delegate to be friendly, and itself produces friendly names } @Override @@ -67,11 +87,12 @@ public Collection nameLocks( final Collection artifacts, final Collection metadatas) { try { - final Path basedir = - DirectoryUtils.resolveDirectory(session, DEFAULT_LOCKS_DIR, CONFIG_PROP_LOCKS_DIR, false); + final Path basedir = basePath != null + ? basePath + : DirectoryUtils.resolveDirectory(session, DEFAULT_LOCKS_DIR, CONFIG_PROP_LOCKS_DIR, false); return delegate.nameLocks(session, artifacts, metadatas).stream() - .map(name -> basedir.resolve(name).toAbsolutePath().toString()) + .map(name -> basedir.resolve(name).toAbsolutePath().toUri().toASCIIString()) .collect(Collectors.toList()); } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/DiscriminatingNameMapper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/DiscriminatingNameMapper.java index 37464e9d9..84e9fae95 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/DiscriminatingNameMapper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/DiscriminatingNameMapper.java @@ -18,9 +18,9 @@ */ package org.eclipse.aether.internal.impl.synccontext.named; -import java.io.File; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.file.Path; import java.util.Collection; import org.eclipse.aether.RepositorySystemSession; @@ -110,7 +110,7 @@ private String createDiscriminator(final RepositorySystemSession session) { if (discriminator == null || discriminator.isEmpty()) { String hostname = ConfigUtils.getString(session, this.hostname, CONFIG_PROP_HOSTNAME); - File basedir = session.getLocalRepository().getBasedir(); + Path basedir = session.getLocalRepository().getBasePath(); discriminator = hostname + ":" + basedir; try { return StringDigestUtil.sha1(discriminator); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java index 556a9f686..dfbf9740e 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/GAVNameMapper.java @@ -29,7 +29,7 @@ /** * Artifact GAV {@link NameMapper}, uses artifact and metadata coordinates to name their corresponding locks. Is not - * considering local repository, only the artifact coordinates. May use custom prefixes and sufixes and separators, + * considering local repository, only the artifact coordinates. May use custom prefixes and suffixes and separators, * hence this instance may or may not be filesystem friendly (depends on strings used). */ public class GAVNameMapper implements NameMapper { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapper.java index 2ec63c846..80b7b0400 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapper.java @@ -31,6 +31,11 @@ public interface NameMapper { /** * Returns {@code true} if lock names returned by this lock name mapper are file system friendly, can be used * as file names and paths. + *

+ * Note: The fact that name mapper is "file system friendly" means ONLY that names it produces CAN be + * used as file names and paths. Still, it does not mean they will work with ANY file based locking, as for example + * {@link org.eclipse.aether.named.providers.FileLockNamedLockFactory} expects names as string encoded + * {@link java.net.URI}s. The only name mapper doing it is {@link BasedirNameMapper}. * * @since 1.9.0 */ 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 afdb8aacd..2098aba60 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,10 +38,7 @@ 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.TestFileProcessor; -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.internal.test.util.*; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; @@ -113,7 +110,7 @@ void setup() { private DefaultArtifactResolver setupArtifactResolver( VersionResolver versionResolver, UpdateCheckManager updateCheckManager) { return new DefaultArtifactResolver( - new TestFileProcessor(), + new TestPathProcessor(), new StubRepositoryEventDispatcher(), versionResolver, updateCheckManager, @@ -365,7 +362,10 @@ public void get( repositoryConnectorProvider.setConnector(connector); resolver = setupArtifactResolver( new StubVersionResolver(), - new DefaultUpdateCheckManager(new DefaultTrackingFileManager(), new DefaultUpdatePolicyAnalyzer())); + new DefaultUpdateCheckManager( + new DefaultTrackingFileManager(), + new DefaultUpdatePolicyAnalyzer(), + new DefaultPathProcessor())); session.setResolutionErrorPolicy(new SimpleResolutionErrorPolicy(true, false)); session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_NEVER); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultChecksumPolicyProviderTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultChecksumPolicyProviderTest.java index cba05a939..af434cc4b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultChecksumPolicyProviderTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultChecksumPolicyProviderTest.java @@ -18,6 +18,8 @@ */ package org.eclipse.aether.internal.impl; +import java.nio.file.Path; + import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.repository.RemoteRepository; @@ -47,7 +49,7 @@ void setup() { session = TestUtils.newSession(); provider = new DefaultChecksumPolicyProvider(); repository = new RemoteRepository.Builder("test", "default", "file:/void").build(); - resource = new TransferResource(repository.getId(), repository.getUrl(), "file.txt", null, null); + resource = new TransferResource(repository.getId(), repository.getUrl(), "file.txt", (Path) null, null); } @AfterEach 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 abe6e5324..7dd083d6e 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 @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -34,8 +35,8 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.deployment.DeployRequest; import org.eclipse.aether.deployment.DeploymentException; -import org.eclipse.aether.internal.test.util.TestFileProcessor; 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; @@ -84,7 +85,7 @@ void setup() throws IOException { connectorProvider = new StubRepositoryConnectorProvider(); deployer = new DefaultDeployer( - new TestFileProcessor(), + new TestPathProcessor(), new StubRepositoryEventDispatcher(), connectorProvider, new StubRemoteRepositoryManager(), @@ -239,6 +240,10 @@ public Metadata setFile(File file) { return this; } + public Metadata setPath(Path path) { + return this; + } + public String getVersion() { return ""; } @@ -259,6 +264,10 @@ public File getFile() { return null; } + public Path getPath() { + return null; + } + public String getArtifactId() { return "aether"; } @@ -275,6 +284,10 @@ public String getProperty(String key, String defaultValue) { return defaultValue; } + public void merge(Path current, Path result) throws RepositoryException { + merge(current != null ? current.toFile() : null, result != null ? result.toFile() : null); + } + public void merge(File current, File result) throws RepositoryException { requireNonNull(current, "current cannot be null"); requireNonNull(result, "result cannot be null"); 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 76f15b713..f279a3b67 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 @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -31,10 +32,7 @@ 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.TestFileProcessor; -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.internal.test.util.*; import org.eclipse.aether.metadata.DefaultMetadata; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.metadata.Metadata.Nature; @@ -86,7 +84,7 @@ void setup() throws IOException { localArtifactFile = new File(session.getLocalRepository().getBasedir(), localArtifactPath); installer = new DefaultInstaller( - new TestFileProcessor(), + new TestPathProcessor(), new StubRepositoryEventDispatcher(), Collections.emptyMap(), new StubSyncContextFactory()); @@ -331,9 +329,9 @@ void testDoNotUpdateUnchangedArtifact() throws InstallationException { installer.install(session, request); installer = new DefaultInstaller( - new DefaultFileProcessor() { + new DefaultPathProcessor() { @Override - public long copy(File src, File target, ProgressListener listener) throws IOException { + public long copy(Path src, Path target, ProgressListener listener) throws IOException { throw new IOException("copy called"); } }, diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java index 6a415c6b6..688656c13 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java @@ -90,7 +90,8 @@ void setup() throws Exception { new StubRemoteRepositoryManager(), new StubSyncContextFactory(), new DefaultOfflineController(), - remoteRepositoryFilterManager); + remoteRepositoryFilterManager, + new DefaultPathProcessor()); repository = new RemoteRepository.Builder( "test-DMRT", "default", @@ -267,7 +268,8 @@ void testFavorLocal() throws IOException { new StubRemoteRepositoryManager(), new StubSyncContextFactory(), new DefaultOfflineController(), - remoteRepositoryFilterManager); + remoteRepositoryFilterManager, + new DefaultPathProcessor()); List results = resolver.resolveMetadata(session, Arrays.asList(request)); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java index 625408880..678ab634d 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManagerTest.java @@ -64,7 +64,7 @@ public class DefaultUpdateCheckManagerTest { @BeforeEach void setup() throws Exception { - File dir = TestFileUtils.createTempFile(""); + File dir = TestFileUtils.createTempDir(""); TestFileUtils.deleteFile(dir); File metadataFile = new File(dir, "metadata.txt"); @@ -78,7 +78,8 @@ void setup() throws Exception { "default", TestFileUtils.createTempDir().toURI().toURL().toString()) .build(); - manager = new DefaultUpdateCheckManager(new DefaultTrackingFileManager(), new DefaultUpdatePolicyAnalyzer()); + manager = new DefaultUpdateCheckManager( + new DefaultTrackingFileManager(), new DefaultUpdatePolicyAnalyzer(), new DefaultPathProcessor()); metadata = new DefaultMetadata( "gid", "aid", "ver", "maven-metadata.xml", Metadata.Nature.RELEASE_OR_SNAPSHOT, metadataFile); artifact = new DefaultArtifact("gid", "aid", "", "ext", "ver").setFile(artifactFile); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java index be51eaa75..1b85146d6 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java @@ -107,7 +107,7 @@ void setup() throws Exception { protected EnhancedLocalRepositoryManager getManager() { return new EnhancedLocalRepositoryManager( - basedir, + basedir.toPath(), new DefaultLocalPathComposer(), "_remote.repositories", trackingFileManager, diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java index 604db15a6..1a500a0c6 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java @@ -31,7 +31,7 @@ public class EnhancedSplitLocalRepositoryManagerTest extends EnhancedLocalReposi protected EnhancedLocalRepositoryManager getManager() { session.setConfigProperty(DefaultLocalPathPrefixComposerFactory.CONFIG_PROP_SPLIT, Boolean.TRUE.toString()); return new EnhancedLocalRepositoryManager( - basedir, + basedir.toPath(), new DefaultLocalPathComposer(), "_remote.repositories", trackingFileManager, diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java index 1eecc5d23..a357ea9b3 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java @@ -18,6 +18,8 @@ */ package org.eclipse.aether.internal.impl; +import java.nio.file.Path; + import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.TransferResource; @@ -34,7 +36,7 @@ public class FailChecksumPolicyTest { @BeforeEach void setup() { - policy = new FailChecksumPolicy(new TransferResource("null", "file:/dev/null", "file.txt", null, null)); + policy = new FailChecksumPolicy(new TransferResource("null", "file:/dev/null", "file.txt", (Path) null, null)); exception = new ChecksumFailureException("test"); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/RecordingRepositoryConnector.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/RecordingRepositoryConnector.java index 257b1e053..091015ce0 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/RecordingRepositoryConnector.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/RecordingRepositoryConnector.java @@ -18,6 +18,7 @@ */ package org.eclipse.aether.internal.impl; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -163,8 +164,8 @@ private void fireInitiated(Transfer transfer) throws Exception { if (listener == null) { return; } - TransferEvent.Builder event = - new TransferEvent.Builder(session, new TransferResource(null, null, null, null, transfer.getTrace())); + TransferEvent.Builder event = new TransferEvent.Builder( + session, new TransferResource(null, null, null, (Path) null, transfer.getTrace())); event.setType(TransferEvent.EventType.INITIATED); listener.transferInitiated(event.build()); } @@ -174,8 +175,8 @@ private void fireDone(Transfer transfer) { if (listener == null) { return; } - TransferEvent.Builder event = - new TransferEvent.Builder(session, new TransferResource(null, null, null, null, transfer.getTrace())); + TransferEvent.Builder event = new TransferEvent.Builder( + session, new TransferResource(null, null, null, (Path) null, transfer.getTrace())); event.setException(transfer.getException()); if (transfer.getException() != null) { listener.transferFailed( diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java index 607e8a787..f17f315a8 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java @@ -48,7 +48,7 @@ public class SimpleLocalRepositoryManagerTest { @BeforeEach void setup() throws IOException { basedir = TestFileUtils.createTempDir("simple-repo"); - manager = new SimpleLocalRepositoryManager(basedir, "simple", new DefaultLocalPathComposer()); + manager = new SimpleLocalRepositoryManager(basedir.toPath(), "simple", new DefaultLocalPathComposer()); session = TestUtils.newSession(); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java index 6550e2f6c..fefea6358 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java @@ -18,6 +18,8 @@ */ package org.eclipse.aether.internal.impl; +import java.nio.file.Path; + import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.TransferResource; @@ -34,7 +36,7 @@ public class WarnChecksumPolicyTest { @BeforeEach void setup() { - policy = new WarnChecksumPolicy(new TransferResource("null", "file:/dev/null", "file.txt", null, null)); + policy = new WarnChecksumPolicy(new TransferResource("null", "file:/dev/null", "file.txt", (Path) null, null)); exception = new ChecksumFailureException("test"); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java index 0dd0a0705..83e812a57 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java @@ -20,13 +20,15 @@ import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.impl.RepositorySystemLifecycle; -import org.eclipse.aether.internal.impl.DefaultFileProcessor; +import org.eclipse.aether.internal.impl.DefaultChecksumProcessor; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; +import org.eclipse.aether.internal.impl.DefaultPathProcessor; public class SparseDirectoryTrustedChecksumsSourceTest extends FileTrustedChecksumsSourceTestSupport { @Override protected FileTrustedChecksumsSourceSupport prepareSubject(RepositorySystemLifecycle lifecycle) { - return new SparseDirectoryTrustedChecksumsSource(new DefaultFileProcessor(), new DefaultLocalPathComposer()); + return new SparseDirectoryTrustedChecksumsSource( + new DefaultChecksumProcessor(new DefaultPathProcessor()), new DefaultLocalPathComposer()); } @Override diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapperTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapperTest.java index 970c2f6fa..95eeed10b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapperTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/BasedirNameMapperTest.java @@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.*; public class BasedirNameMapperTest extends NameMapperTestSupport { - private final String PS = File.separator; + private final String PS = "/"; // we work with URIs now, not OS file paths BasedirNameMapper mapper = new BasedirNameMapper(new HashingNameMapper(GAVNameMapper.gav())); @@ -62,7 +62,8 @@ void defaultLocksDir() { Collection names = mapper.nameLocks(session, singletonList(artifact), null); assertEquals(names.size(), 1); assertEquals( - names.iterator().next(), basedir + PS + ".locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); + names.iterator().next(), + basedir.toUri() + PS + ".locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); } @Test @@ -74,13 +75,14 @@ void relativeLocksDir() { assertEquals(names.size(), 1); assertEquals( names.iterator().next(), - basedir + PS + "my" + PS + "locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); + basedir.toUri() + PS + "my" + PS + "locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); } @Test void absoluteLocksDir() throws IOException { String absoluteLocksDir = "/my/locks"; - String customBaseDir = new File(absoluteLocksDir).getCanonicalPath(); + String customBaseDir = + new File(absoluteLocksDir).getCanonicalFile().toPath().toUri().toASCIIString(); configProperties.put("aether.syncContext.named.hashing.depth", "0"); configProperties.put("aether.syncContext.named.basedir.locksDir", absoluteLocksDir); @@ -98,7 +100,8 @@ void singleArtifact() { Collection names = mapper.nameLocks(session, singletonList(artifact), null); assertEquals(names.size(), 1); assertEquals( - names.iterator().next(), basedir + PS + ".locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); + names.iterator().next(), + basedir.toUri() + PS + ".locks" + PS + "46e98183d232f1e16f863025080c7f2b9797fd10"); } @Test @@ -110,7 +113,8 @@ void singleMetadata() { Collection names = mapper.nameLocks(session, null, singletonList(metadata)); assertEquals(names.size(), 1); assertEquals( - names.iterator().next(), basedir + PS + ".locks" + PS + "293b3990971f4b4b02b220620d2538eaac5f221b"); + names.iterator().next(), + basedir.toUri() + PS + ".locks" + PS + "293b3990971f4b4b02b220620d2538eaac5f221b"); } @Test @@ -126,7 +130,11 @@ void oneAndOne() { Iterator namesIterator = names.iterator(); // they are sorted as well - assertEquals(namesIterator.next(), basedir + PS + ".locks" + PS + "d36504431d00d1c6e4d1c34258f2bf0a004de085"); - assertEquals(namesIterator.next(), basedir + PS + ".locks" + PS + "fbcebba60d7eb931eca634f6ca494a8a1701b638"); + assertEquals( + namesIterator.next(), + basedir.toUri() + PS + ".locks" + PS + "d36504431d00d1c6e4d1c34258f2bf0a004de085"); + assertEquals( + namesIterator.next(), + basedir.toUri() + PS + ".locks" + PS + "fbcebba60d7eb931eca634f6ca494a8a1701b638"); } } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapperTestSupport.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapperTestSupport.java index 0daad74f2..829513aec 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapperTestSupport.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/synccontext/named/NameMapperTestSupport.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.HashMap; import org.eclipse.aether.RepositorySystemSession; @@ -33,7 +34,7 @@ * Simple support class for {@link NameMapper} implementation UTs. */ public abstract class NameMapperTestSupport { - protected String basedir; + protected Path basedir; protected HashMap configProperties; @@ -41,10 +42,10 @@ public abstract class NameMapperTestSupport { @BeforeEach void before() throws IOException { - basedir = new File("/home/maven/.m2/repository").getCanonicalPath(); + basedir = new File("/home/maven/.m2/repository").getCanonicalFile().toPath(); configProperties = new HashMap<>(); - LocalRepository localRepository = new LocalRepository(new File(basedir)); + LocalRepository localRepository = new LocalRepository(basedir); session = mock(RepositorySystemSession.class); when(session.getConfigProperties()).thenReturn(configProperties); when(session.getLocalRepository()).thenReturn(localRepository); diff --git a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/providers/FileLockNamedLockFactory.java b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/providers/FileLockNamedLockFactory.java index 86ce77498..9d4ef472e 100644 --- a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/providers/FileLockNamedLockFactory.java +++ b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/providers/FileLockNamedLockFactory.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.net.URI; import java.nio.channels.FileChannel; import java.nio.file.AccessDeniedException; import java.nio.file.Files; @@ -39,8 +40,9 @@ import static org.eclipse.aether.named.support.Retry.retry; /** - * Named locks factory of {@link FileLockNamedLock}s. This is a bit special implementation, as it - * expects locks names to be fully qualified absolute file system paths. + * Named locks factory of {@link FileLockNamedLock}s. This is a bit of special implementation, as it + * expects locks names to be proper URI string representations (use {@code file:} protocol for default + * file system). * * @since 1.7.3 */ @@ -98,7 +100,7 @@ public FileLockNamedLockFactory() { @Override protected NamedLockSupport createLock(final String name) { - Path path = Paths.get(name); + Path path = Paths.get(URI.create(name)); FileChannel fileChannel = fileChannels.computeIfAbsent(name, k -> { try { Files.createDirectories(path.getParent()); diff --git a/maven-resolver-named-locks/src/test/java/org/eclipse/aether/named/FileLockNamedLockFactorySupportTest.java b/maven-resolver-named-locks/src/test/java/org/eclipse/aether/named/FileLockNamedLockFactorySupportTest.java index b538ddcc7..9f37ca586 100644 --- a/maven-resolver-named-locks/src/test/java/org/eclipse/aether/named/FileLockNamedLockFactorySupportTest.java +++ b/maven-resolver-named-locks/src/test/java/org/eclipse/aether/named/FileLockNamedLockFactorySupportTest.java @@ -39,7 +39,10 @@ public FileLockNamedLockFactorySupportTest() throws IOException { @Override protected String lockName(TestInfo testInfo) { - return baseDir.resolve(testInfo.getDisplayName()).toAbsolutePath().toString(); + return baseDir.resolve(testInfo.getDisplayName()) + .toAbsolutePath() + .toUri() + .toASCIIString(); } @BeforeAll diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactDownload.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactDownload.java index 36eb4c03b..c76206d7f 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactDownload.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactDownload.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -60,7 +61,9 @@ public ArtifactDownload() { * @param context The context in which this download is performed, may be {@code null}. * @param file The local file to download the artifact to, may be {@code null}. * @param checksumPolicy The checksum policy, may be {@code null}. + * @deprecated Use {@link ArtifactDownload(Artifact, String, Path, String)} instead. */ + @Deprecated public ArtifactDownload(Artifact artifact, String context, File file, String checksumPolicy) { setArtifact(artifact); setRequestContext(context); @@ -68,18 +71,42 @@ public ArtifactDownload(Artifact artifact, String context, File file, String che setChecksumPolicy(checksumPolicy); } + /** + * Creates a new download with the specified properties. + * + * @param artifact The artifact to download, may be {@code null}. + * @param context The context in which this download is performed, may be {@code null}. + * @param path The local file to download the artifact to, may be {@code null}. + * @param checksumPolicy The checksum policy, may be {@code null}. + * @deprecated Use {@link ArtifactDownload(Artifact, String, Path, String)} instead. + * @since 2.0.0 + */ + public ArtifactDownload(Artifact artifact, String context, Path path, String checksumPolicy) { + setArtifact(artifact); + setRequestContext(context); + setPath(path); + setChecksumPolicy(checksumPolicy); + } + @Override public ArtifactDownload setArtifact(Artifact artifact) { super.setArtifact(artifact); return this; } + @Deprecated @Override public ArtifactDownload setFile(File file) { super.setFile(file); return this; } + @Override + public ArtifactDownload setPath(Path path) { + super.setPath(path); + return this; + } + /** * Indicates whether this transfer shall only verify the existence of the artifact in the remote repository rather * than actually downloading the file. Just like with an actual transfer, a connector is expected to signal the @@ -222,6 +249,6 @@ public ArtifactDownload setTrace(RequestTrace trace) { @Override public String toString() { - return getArtifact() + " - " + (isExistenceCheck() ? "?" : "") + getFile(); + return getArtifact() + " - " + (isExistenceCheck() ? "?" : "") + getPath(); } } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactTransfer.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactTransfer.java index 979098b61..80fe7efe2 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactTransfer.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactTransfer.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.transfer.ArtifactTransferException; @@ -32,7 +33,7 @@ public abstract class ArtifactTransfer extends Transfer { private Artifact artifact; - private File file; + private Path path; private ArtifactTransferException exception; @@ -66,9 +67,23 @@ public ArtifactTransfer setArtifact(Artifact artifact) { * completed such that an interrupted/failed download does not corrupt the current file contents. * * @return The local file or {@code null} if not set. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the local file the artifact is downloaded to or uploaded from. In case of a download, a connector should + * first transfer the bytes to a temporary file and only overwrite the target file once the entire download is + * completed such that an interrupted/failed download does not corrupt the current file contents. + * + * @return The local file or {@code null} if not set. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -76,9 +91,22 @@ public File getFile() { * * @param file The local file, may be {@code null}. * @return This transfer for chaining, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public ArtifactTransfer setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the local file the artifact is downloaded to or uploaded from. + * + * @param path The local file, may be {@code null}. + * @return This transfer for chaining, never {@code null}. + * @since 2.0.0 + */ + public ArtifactTransfer setPath(Path path) { + this.path = path; return this; } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java index 81530621a..496dff9a8 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.RequestTrace; import org.eclipse.aether.artifact.Artifact; @@ -42,24 +43,45 @@ public ArtifactUpload() { * * @param artifact The artifact to upload, may be {@code null}. * @param file The local file to upload the artifact from, may be {@code null}. + * @deprecated Use {@link #ArtifactUpload(Artifact, Path)} instead. */ + @Deprecated public ArtifactUpload(Artifact artifact, File file) { setArtifact(artifact); setFile(file); } + /** + * Creates a new upload with the specified properties. + * + * @param artifact The artifact to upload, may be {@code null}. + * @param path The local file to upload the artifact from, may be {@code null}. + * @since 2.0.0 + */ + public ArtifactUpload(Artifact artifact, Path path) { + setArtifact(artifact); + setPath(path); + } + @Override public ArtifactUpload setArtifact(Artifact artifact) { super.setArtifact(artifact); return this; } + @Deprecated @Override public ArtifactUpload setFile(File file) { super.setFile(file); return this; } + @Override + public ArtifactUpload setPath(Path path) { + super.setPath(path); + return this; + } + @Override public ArtifactUpload setException(ArtifactTransferException exception) { super.setException(exception); @@ -80,6 +102,6 @@ public ArtifactUpload setTrace(RequestTrace trace) { @Override public String toString() { - return getArtifact() + " - " + getFile(); + return getArtifact() + " - " + getPath(); } } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataDownload.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataDownload.java index cc77497fe..5bb38a6bb 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataDownload.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataDownload.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -54,10 +55,25 @@ public MetadataDownload() { * @param context The context in which this download is performed, may be {@code null}. * @param file The local file to download the metadata to, may be {@code null}. * @param checksumPolicy The checksum policy, may be {@code null}. + * @deprecated Use {@link #MetadataDownload(Metadata, String, Path, String)} instead. */ + @Deprecated public MetadataDownload(Metadata metadata, String context, File file, String checksumPolicy) { + this(metadata, context, file != null ? file.toPath() : null, checksumPolicy); + } + + /** + * Creates a new download with the specified properties. + * + * @param metadata The metadata to download, may be {@code null}. + * @param context The context in which this download is performed, may be {@code null}. + * @param path The local file to download the metadata to, may be {@code null}. + * @param checksumPolicy The checksum policy, may be {@code null}. + * @since 2.0.0 + */ + public MetadataDownload(Metadata metadata, String context, Path path, String checksumPolicy) { setMetadata(metadata); - setFile(file); + setPath(path); setChecksumPolicy(checksumPolicy); setRequestContext(context); } @@ -68,12 +84,19 @@ public MetadataDownload setMetadata(Metadata metadata) { return this; } + @Deprecated @Override public MetadataDownload setFile(File file) { super.setFile(file); return this; } + @Override + public MetadataDownload setPath(Path path) { + super.setPath(path); + return this; + } + /** * Gets the checksum policy for this transfer. * @@ -160,6 +183,6 @@ public MetadataDownload setTrace(RequestTrace trace) { @Override public String toString() { - return getMetadata() + " - " + getFile(); + return getMetadata() + " - " + getPath(); } } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataTransfer.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataTransfer.java index b1be3fab2..c9976ebf4 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataTransfer.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataTransfer.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.transfer.MetadataTransferException; @@ -32,7 +33,7 @@ public abstract class MetadataTransfer extends Transfer { private Metadata metadata; - private File file; + private Path path; private MetadataTransferException exception; @@ -66,9 +67,23 @@ public MetadataTransfer setMetadata(Metadata metadata) { * completed such that an interrupted/failed download does not corrupt the current file contents. * * @return The local file or {@code null} if not set. + * @deprecated Use {@link #getPath()} instead. */ + @Deprecated public File getFile() { - return file; + return path != null ? path.toFile() : null; + } + + /** + * Gets the local file the metadata is downloaded to or uploaded from. In case of a download, a connector should + * first transfer the bytes to a temporary file and only overwrite the target file once the entire download is + * completed such that an interrupted/failed download does not corrupt the current file contents. + * + * @return The local file or {@code null} if not set. + * @since 2.0.0 + */ + public Path getPath() { + return path; } /** @@ -76,9 +91,22 @@ public File getFile() { * * @param file The local file, may be {@code null}. * @return This transfer for chaining, never {@code null}. + * @deprecated Use {@link #setPath(Path)} instead. */ + @Deprecated public MetadataTransfer setFile(File file) { - this.file = file; + return setPath(file != null ? file.toPath() : null); + } + + /** + * Sets the local file the metadata is downloaded to or uploaded from. + * + * @param path The local file, may be {@code null}. + * @return This transfer for chaining, never {@code null}. + * @since 2.0.0 + */ + public MetadataTransfer setPath(Path path) { + this.path = path; return this; } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataUpload.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataUpload.java index d549f7ac6..ebaaaaca1 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataUpload.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/MetadataUpload.java @@ -19,6 +19,7 @@ package org.eclipse.aether.spi.connector; import java.io.File; +import java.nio.file.Path; import org.eclipse.aether.RequestTrace; import org.eclipse.aether.metadata.Metadata; @@ -43,24 +44,45 @@ public MetadataUpload() { * * @param metadata The metadata to upload, may be {@code null}. * @param file The local file to upload the metadata from, may be {@code null}. + * @deprecated Use {@link #MetadataUpload(Metadata, Path)} instead. */ + @Deprecated public MetadataUpload(Metadata metadata, File file) { setMetadata(metadata); setFile(file); } + /** + * Creates a new upload with the specified properties. + * + * @param metadata The metadata to upload, may be {@code null}. + * @param path The local file to upload the metadata from, may be {@code null}. + * @since 2.0.0 + */ + public MetadataUpload(Metadata metadata, Path path) { + setMetadata(metadata); + setPath(path); + } + @Override public MetadataUpload setMetadata(Metadata metadata) { super.setMetadata(metadata); return this; } + @Deprecated @Override public MetadataUpload setFile(File file) { super.setFile(file); return this; } + @Override + public MetadataUpload setPath(Path path) { + super.setPath(path); + return this; + } + @Override public MetadataUpload setException(MetadataTransferException exception) { super.setException(exception); @@ -81,6 +103,6 @@ public MetadataUpload setTrace(RequestTrace trace) { @Override public String toString() { - return getMetadata() + " - " + getFile(); + return getMetadata() + " - " + getPath(); } } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java index ebcc3131f..8e311cd86 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -66,7 +67,22 @@ public static Map calculate(byte[] data, List calculate(File file, List factories) throws IOException { - try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file.toPath()))) { + return calculate(file.toPath(), factories); + } + + /** + * Calculates checksums for specified file. + * + * @param path The file for which to calculate checksums, must not be {@code null}. + * @param factories The checksum algorithm factories to use, must not be {@code null}. + * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to + * calculate it, never {@code null}. + * @throws IOException In case of any problem. + * @since 2.0.0 + */ + public static Map calculate(Path path, List factories) + throws IOException { + try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(path))) { return calculate(inputStream, factories); } } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java index bfc873a26..19b3d798a 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java @@ -22,9 +22,11 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collections; import java.util.HashMap; @@ -37,7 +39,7 @@ */ public final class GetTask extends TransportTask { - private File dataFile; + private Path dataPath; private boolean resume; @@ -78,16 +80,13 @@ public OutputStream newOutputStream() throws IOException { * @throws IOException If the stream could not be opened. */ public OutputStream newOutputStream(boolean resume) throws IOException { - if (dataFile != null) { + if (dataPath != null) { if (this.resume && resume) { return Files.newOutputStream( - dataFile.toPath(), - StandardOpenOption.CREATE, - StandardOpenOption.WRITE, - StandardOpenOption.APPEND); + dataPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND); } else { return Files.newOutputStream( - dataFile.toPath(), + dataPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); @@ -106,9 +105,22 @@ public OutputStream newOutputStream(boolean resume) throws IOException { * be overwritten. * * @return The data file or {@code null} if the data will be buffered in memory. + * @deprecated Use {@link #getDataPath()} instead. */ + @Deprecated public File getDataFile() { - return dataFile; + return dataPath != null ? dataPath.toFile() : null; + } + + /** + * Gets the file (if any) where the downloaded data should be stored. If the specified file already exists, it will + * be overwritten. + * + * @return The data file or {@code null} if the data will be buffered in memory. + * @since 2.0.0 + */ + public Path getDataPath() { + return dataPath; } /** @@ -118,11 +130,26 @@ public File getDataFile() { * * @param dataFile The file to store the downloaded data, may be {@code null} to store the data in memory. * @return This task for chaining, never {@code null}. + * @deprecated Use {@link #setDataPath(Path)} instead. */ + @Deprecated public GetTask setDataFile(File dataFile) { return setDataFile(dataFile, false); } + /** + * Sets the file where the downloaded data should be stored. If the specified file already exists, it will be + * overwritten. Unless the caller can reasonably expect the resource to be small, use of a data file is strongly + * recommended to avoid exhausting heap memory during the download. + * + * @param dataPath The file to store the downloaded data, may be {@code null} to store the data in memory. + * @return This task for chaining, never {@code null}. + * @since 2.0.0 + */ + public GetTask setDataPath(Path dataPath) { + return setDataPath(dataPath, false); + } + /** * Sets the file where the downloaded data should be stored. If the specified file already exists, it will be * overwritten or appended to, depending on the {@code resume} argument and the capabilities of the transporter. @@ -133,9 +160,27 @@ public GetTask setDataFile(File dataFile) { * @param resume {@code true} to request resuming a previous download attempt, starting from the current length of * the data file, {@code false} to download the resource from its beginning. * @return This task for chaining, never {@code null}. + * @deprecated Use {@link #setDataPath(Path, boolean)} instead. */ + @Deprecated public GetTask setDataFile(File dataFile, boolean resume) { - this.dataFile = dataFile; + return setDataPath(dataFile != null ? dataFile.toPath() : null, resume); + } + + /** + * Sets the file where the downloaded data should be stored. If the specified file already exists, it will be + * overwritten or appended to, depending on the {@code resume} argument and the capabilities of the transporter. + * Unless the caller can reasonably expect the resource to be small, use of a data file is strongly recommended to + * avoid exhausting heap memory during the download. + * + * @param dataPath The file to store the downloaded data, may be {@code null} to store the data in memory. + * @param resume {@code true} to request resuming a previous download attempt, starting from the current length of + * the data file, {@code false} to download the resource from its beginning. + * @return This task for chaining, never {@code null}. + * @since 2.0.0 + */ + public GetTask setDataPath(Path dataPath, boolean resume) { + this.dataPath = dataPath; this.resume = resume; return this; } @@ -148,8 +193,12 @@ public GetTask setDataFile(File dataFile, boolean resume) { */ public long getResumeOffset() { if (resume) { - if (dataFile != null) { - return dataFile.length(); + if (dataPath != null) { + try { + return Files.size(dataPath); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } if (dataBytes != null) { return dataBytes.size(); @@ -165,7 +214,7 @@ public long getResumeOffset() { * @return The possibly empty data bytes, never {@code null}. */ public byte[] getDataBytes() { - if (dataFile != null || dataBytes == null) { + if (dataPath != null || dataBytes == null) { return EMPTY; } return dataBytes.toByteArray(); @@ -179,7 +228,7 @@ public byte[] getDataBytes() { * @return The possibly empty data string, never {@code null}. */ public String getDataString() { - if (dataFile != null || dataBytes == null) { + if (dataPath != null || dataBytes == null) { return ""; } return new String(dataBytes.toByteArray(), StandardCharsets.UTF_8); diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PutTask.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PutTask.java index 273ab1135..5860e0441 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PutTask.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PutTask.java @@ -18,13 +18,11 @@ */ package org.eclipse.aether.spi.connector.transport; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; /** * A task to upload a resource to the remote repository. @@ -33,7 +31,7 @@ */ public final class PutTask extends TransportTask { - private File dataFile; + private Path dataPath; private byte[] dataBytes = EMPTY; @@ -54,8 +52,8 @@ public PutTask(URI location) { * @throws IOException If the stream could not be opened. */ public InputStream newInputStream() throws IOException { - if (dataFile != null) { - return Files.newInputStream(dataFile.toPath()); + if (dataPath != null) { + return Files.newInputStream(dataPath); } return new ByteArrayInputStream(dataBytes); } @@ -66,8 +64,12 @@ public InputStream newInputStream() throws IOException { * @return The total number of bytes to be uploaded. */ public long getDataLength() { - if (dataFile != null) { - return dataFile.length(); + if (dataPath != null) { + try { + return Files.size(dataPath); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } return dataBytes.length; } @@ -76,9 +78,21 @@ public long getDataLength() { * Gets the file (if any) with the data to be uploaded. * * @return The data file or {@code null} if the data resides in memory. + * @deprecated Use {@link #getDataPath()} instead. */ + @Deprecated public File getDataFile() { - return dataFile; + return dataPath != null ? dataPath.toFile() : null; + } + + /** + * Gets the file (if any) with the data to be uploaded. + * + * @return The data file or {@code null} if the data resides in memory. + * @since 2.0.0 + */ + public Path getDataPath() { + return dataPath; } /** @@ -87,9 +101,23 @@ public File getDataFile() { * * @param dataFile The data file, may be {@code null} if the resource data is provided directly from memory. * @return This task for chaining, never {@code null}. + * @deprecated Use {@link #setDataPath(Path)} instead. */ + @Deprecated public PutTask setDataFile(File dataFile) { - this.dataFile = dataFile; + return setDataPath(dataFile.toPath()); + } + + /** + * Sets the file with the data to be uploaded. To upload some data residing already in memory, use + * {@link #setDataString(String)} or {@link #setDataBytes(byte[])}. + * + * @param dataPath The data file, may be {@code null} if the resource data is provided directly from memory. + * @return This task for chaining, never {@code null}. + * @since 2.0.0 + */ + public PutTask setDataPath(Path dataPath) { + this.dataPath = dataPath; dataBytes = EMPTY; return this; } @@ -102,7 +130,7 @@ public PutTask setDataFile(File dataFile) { */ public PutTask setDataBytes(byte[] bytes) { this.dataBytes = (bytes != null) ? bytes : EMPTY; - dataFile = null; + dataPath = null; return this; } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/ChecksumProcessor.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/ChecksumProcessor.java new file mode 100644 index 000000000..e9dcb3e97 --- /dev/null +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/ChecksumProcessor.java @@ -0,0 +1,45 @@ +/* + * 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.IOException; +import java.nio.file.Path; + +/** + * A utility component to perform checksum related operations. + * + * @since 2.0.0 + */ +public interface ChecksumProcessor { + /** + * Reads checksum from specified file. + * + * @throws IOException in case of any IO error. + * @since 1.8.0 + */ + String readChecksum(Path checksumFile) throws IOException; + + /** + * Writes checksum to specified file. + * + * @throws IOException in case of any IO error. + * @since 1.8.0 + */ + void writeChecksum(Path checksumFile, String checksum) throws IOException; +} diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java index 98a60658c..6ad9512d6 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java @@ -25,7 +25,10 @@ /** * A utility component to perform file-based operations. + * + * @deprecated Use {@link PathProcessor} or {@link ChecksumProcessor} instead. */ +@Deprecated public interface FileProcessor { /** @@ -97,7 +100,7 @@ public interface FileProcessor { * * @see FileProcessor#copy(File, File, ProgressListener) */ - interface ProgressListener { + interface ProgressListener extends PathProcessor.ProgressListener { void progressed(ByteBuffer buffer) throws IOException; } 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 new file mode 100644 index 000000000..9b28ec2d6 --- /dev/null +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/PathProcessor.java @@ -0,0 +1,127 @@ +/* + * 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.*; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +/** + * A utility component to perform file-based operations. + * + * @since 2.0.0 + */ +public interface PathProcessor { + /** + * Returns last modified of path in milliseconds, if exists. + * + * @param path The path, may be {@code null}. + * @throws UncheckedIOException If an I/O error occurs. + */ + default long lastModified(Path path, long defValue) { + try { + return Files.getLastModifiedTime(path).toMillis(); + } catch (NoSuchFileException e) { + return defValue; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Returns size of file, if exists. + * + * @param path The path, may be {@code null}. + * @throws UncheckedIOException If an I/O error occurs. + */ + default long size(Path path, long defValue) { + try { + return Files.size(path); + } catch (NoSuchFileException e) { + return defValue; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * 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. + */ + void write(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. + */ + void write(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 + * on the file system. + * + * @param source The file to move from, must not be {@code null}. + * @param target The file to move to, must not be {@code null}. + * @throws IOException If an I/O error occurs. + */ + void move(Path source, Path target) throws IOException; + + /** + * Copies the specified source file to the given target 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 source The file to copy from, must not be {@code null}. + * @param target The file to copy to, must not be {@code null}. + * @throws IOException If an I/O error occurs. + */ + void copy(Path source, Path target) throws IOException; + + /** + * Copies the specified source file to the given target 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 source The file to copy from, must not be {@code null}. + * @param target The file to copy to, must not be {@code null}. + * @param listener The listener to notify about the copy progress, may be {@code null}. + * @return The number of copied bytes. + * @throws IOException If an I/O error occurs. + */ + long copy(Path source, Path target, ProgressListener listener) throws IOException; + + /** + * A listener object that is notified for every progress made while copying files. + * + * @see PathProcessor#copy(Path, Path, ProgressListener) + */ + interface ProgressListener { + + void progressed(ByteBuffer buffer) throws IOException; + } +} diff --git a/maven-resolver-supplier/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index 7e1807909..561535d4a 100644 --- a/maven-resolver-supplier/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -93,7 +93,8 @@ import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; @@ -136,18 +137,32 @@ private void checkClosed() { } } - private FileProcessor fileProcessor; + private PathProcessor pathProcessor; - public final FileProcessor getFileProcessor() { + public final PathProcessor getPathProcessor() { checkClosed(); - if (fileProcessor == null) { - fileProcessor = createFileProcessor(); + if (pathProcessor == null) { + pathProcessor = createPathProcessor(); } - return fileProcessor; + return pathProcessor; } - protected FileProcessor createFileProcessor() { - return new DefaultFileProcessor(); + protected PathProcessor createPathProcessor() { + return new DefaultPathProcessor(); + } + + private ChecksumProcessor checksumProcessor; + + public final ChecksumProcessor getChecksumProcessor() { + checkClosed(); + if (checksumProcessor == null) { + checksumProcessor = createChecksumProcessor(); + } + return checksumProcessor; + } + + protected ChecksumProcessor createChecksumProcessor() { + return new DefaultChecksumProcessor(getPathProcessor()); } private TrackingFileManager trackingFileManager; @@ -260,7 +275,7 @@ public final UpdateCheckManager getUpdateCheckManager() { } protected UpdateCheckManager createUpdateCheckManager() { - return new DefaultUpdateCheckManager(getTrackingFileManager(), getUpdatePolicyAnalyzer()); + return new DefaultUpdateCheckManager(getTrackingFileManager(), getUpdatePolicyAnalyzer(), getPathProcessor()); } private Map namedLockFactories; @@ -509,7 +524,7 @@ protected Map createTrustedChecksumsSources() { HashMap result = new HashMap<>(); result.put( SparseDirectoryTrustedChecksumsSource.NAME, - new SparseDirectoryTrustedChecksumsSource(getFileProcessor(), getLocalPathComposer())); + new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); result.put( SummaryFileTrustedChecksumsSource.NAME, new SummaryFileTrustedChecksumsSource(getLocalPathComposer(), getRepositorySystemLifecycle())); @@ -611,7 +626,7 @@ protected BasicRepositoryConnectorFactory createBasicRepositoryConnectorFactory( getTransporterProvider(), getRepositoryLayoutProvider(), getChecksumPolicyProvider(), - getFileProcessor(), + getChecksumProcessor(), getProvidedChecksumsSources()); } @@ -658,7 +673,7 @@ public final Installer getInstaller() { protected Installer createInstaller() { return new DefaultInstaller( - getFileProcessor(), + getPathProcessor(), getRepositoryEventDispatcher(), getMetadataGeneratorFactories(), getSyncContextFactory()); @@ -676,7 +691,7 @@ public final Deployer getDeployer() { protected Deployer createDeployer() { return new DefaultDeployer( - getFileProcessor(), + getPathProcessor(), getRepositoryEventDispatcher(), getRepositoryConnectorProvider(), getRemoteRepositoryManager(), @@ -755,7 +770,7 @@ public final ArtifactResolver getArtifactResolver() { protected ArtifactResolver createArtifactResolver() { return new DefaultArtifactResolver( - getFileProcessor(), + getPathProcessor(), getRepositoryEventDispatcher(), getVersionResolver(), getUpdateCheckManager(), @@ -785,7 +800,8 @@ protected MetadataResolver createMetadataResolver() { getRemoteRepositoryManager(), getSyncContextFactory(), getOfflineController(), - getRemoteRepositoryFilterManager()); + getRemoteRepositoryFilterManager(), + getPathProcessor()); } private VersionScheme versionScheme; diff --git a/maven-resolver-supplier/src/test/java/org/eclipse/aether/supplier/RepositorySystemSupplierTest.java b/maven-resolver-supplier/src/test/java/org/eclipse/aether/supplier/RepositorySystemSupplierTest.java index 77e793cab..b3ac64c6b 100644 --- a/maven-resolver-supplier/src/test/java/org/eclipse/aether/supplier/RepositorySystemSupplierTest.java +++ b/maven-resolver-supplier/src/test/java/org/eclipse/aether/supplier/RepositorySystemSupplierTest.java @@ -30,7 +30,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.VersionRangeRequest; import org.eclipse.aether.resolution.VersionRangeResult; -import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.version.Version; import org.junit.jupiter.api.Test; @@ -42,7 +42,7 @@ void smoke() throws Exception { try (RepositorySystem system = new RepositorySystemSupplier().get(); CloseableSession session = new SessionBuilderSupplier(system) .get() - .withLocalRepositoryBaseDirectories(new File("target/local-repo")) + .withLocalRepositoryBaseDirectories(new File("target/local-repo").toPath()) .build()) { Artifact artifact = new DefaultArtifact("org.apache.maven.resolver:maven-resolver-util:[0,)"); VersionRangeRequest rangeRequest = new VersionRangeRequest(); @@ -65,22 +65,22 @@ void closed() { systemSupplier.get().close(); // get an instance and immediately shut it down, this closes supplier as well assertThrows(IllegalStateException.class, systemSupplier::get); assertThrows(IllegalStateException.class, systemSupplier::getRepositorySystem); - assertThrows(IllegalStateException.class, systemSupplier::getFileProcessor); + assertThrows(IllegalStateException.class, systemSupplier::getChecksumProcessor); } @Test void memorizing() { RepositorySystemSupplier systemSupplier = new RepositorySystemSupplier(); - FileProcessor fileProcessor = systemSupplier.getFileProcessor(); + PathProcessor pathProcessor = systemSupplier.getPathProcessor(); Deployer deployer = systemSupplier.getDeployer(); try (RepositorySystem repositorySystem = systemSupplier.get()) { assertSame(systemSupplier.get(), repositorySystem); - assertSame(systemSupplier.getFileProcessor(), fileProcessor); + assertSame(systemSupplier.getPathProcessor(), pathProcessor); assertSame(systemSupplier.getDeployer(), deployer); } assertThrows(IllegalStateException.class, systemSupplier::get); assertThrows(IllegalStateException.class, systemSupplier::getRepositorySystem); assertThrows(IllegalStateException.class, systemSupplier::getDeployer); - assertThrows(IllegalStateException.class, systemSupplier::getFileProcessor); + assertThrows(IllegalStateException.class, systemSupplier::getPathProcessor); } } diff --git a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java index 0b0a74afe..c7ab69859 100644 --- a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java +++ b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java @@ -344,8 +344,9 @@ protected void testGet_ToMemory() throws Exception { protected void testGet_ToFile() throws Exception { File file = TestFileUtils.createTempFile("failure"); RecordingTransportListener listener = new RecordingTransportListener(); - GetTask task = - new GetTask(URI.create("repo/file.txt")).setDataFile(file).setListener(listener); + GetTask task = new GetTask(URI.create("repo/file.txt")) + .setDataPath(file.toPath()) + .setListener(listener); transporter.get(task); assertEquals("test", TestFileUtils.readString(file)); assertEquals(0L, listener.getDataOffset()); @@ -360,7 +361,7 @@ protected void testGet_ToFileTimestamp() throws Exception { File file = TestFileUtils.createTempFile("failure"); RecordingTransportListener listener = new RecordingTransportListener(); GetTask task = new GetTask(URI.create("repo/dir/oldFile.txt")) - .setDataFile(file) + .setDataPath(file.toPath()) .setListener(listener); transporter.get(task); assertEquals("oldTest", TestFileUtils.readString(file)); @@ -376,8 +377,9 @@ protected void testGet_ToFileTimestamp() throws Exception { protected void testGet_EmptyResource() throws Exception { File file = TestFileUtils.createTempFile("failure"); RecordingTransportListener listener = new RecordingTransportListener(); - GetTask task = - new GetTask(URI.create("repo/empty.txt")).setDataFile(file).setListener(listener); + GetTask task = new GetTask(URI.create("repo/empty.txt")) + .setDataPath(file.toPath()) + .setListener(listener); transporter.get(task); assertEquals("", TestFileUtils.readString(file)); assertEquals(0L, listener.getDataOffset()); @@ -546,7 +548,7 @@ protected void testGet_Resume() throws Exception { File file = TestFileUtils.createTempFile("re"); RecordingTransportListener listener = new RecordingTransportListener(); GetTask task = new GetTask(URI.create("repo/resume.txt")) - .setDataFile(file, true) + .setDataPath(file.toPath(), true) .setListener(listener); transporter.get(task); assertEquals("resumable", TestFileUtils.readString(file)); @@ -563,7 +565,7 @@ protected void testGet_ResumeLocalContentsOutdated() throws Exception { file.setLastModified(System.currentTimeMillis() - 5 * 60 * 1000); RecordingTransportListener listener = new RecordingTransportListener(); GetTask task = new GetTask(URI.create("repo/resume.txt")) - .setDataFile(file, true) + .setDataPath(file.toPath(), true) .setListener(listener); transporter.get(task); assertEquals("resumable", TestFileUtils.readString(file)); @@ -580,7 +582,7 @@ protected void testGet_ResumeRangesNotSupportedByServer() throws Exception { File file = TestFileUtils.createTempFile("re"); RecordingTransportListener listener = new RecordingTransportListener(); GetTask task = new GetTask(URI.create("repo/resume.txt")) - .setDataFile(file, true) + .setDataPath(file.toPath(), true) .setListener(listener); transporter.get(task); assertEquals("resumable", TestFileUtils.readString(file)); @@ -615,7 +617,7 @@ protected void testGet_Checksums_XChecksum() throws Exception { protected void testGet_FileHandleLeak() throws Exception { for (int i = 0; i < 100; i++) { File file = TestFileUtils.createTempFile("failure"); - transporter.get(new GetTask(URI.create("repo/file.txt")).setDataFile(file)); + transporter.get(new GetTask(URI.create("repo/file.txt")).setDataPath(file.toPath())); assertTrue(file.delete(), i + ", " + file.getAbsolutePath()); } } @@ -694,7 +696,7 @@ protected void testPut_FromFile() throws Exception { File file = TestFileUtils.createTempFile("upload"); RecordingTransportListener listener = new RecordingTransportListener(); PutTask task = - new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataFile(file); + new PutTask(URI.create("repo/file.txt")).setListener(listener).setDataPath(file.toPath()); transporter.put(task); assertEquals(0L, listener.getDataOffset()); assertEquals(6L, listener.getDataLength()); @@ -915,7 +917,7 @@ protected void testPut_FileHandleLeak() throws Exception { for (int i = 0; i < 100; i++) { File src = TestFileUtils.createTempFile("upload"); File dst = new File(repoDir, "file.txt"); - transporter.put(new PutTask(URI.create("repo/file.txt")).setDataFile(src)); + transporter.put(new PutTask(URI.create("repo/file.txt")).setDataPath(src.toPath())); assertTrue(src.delete(), i + ", " + src.getAbsolutePath()); assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath()); } diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumProcessor.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumProcessor.java new file mode 100644 index 000000000..34899260b --- /dev/null +++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumProcessor.java @@ -0,0 +1,41 @@ +/* + * 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.internal.test.util; + +import java.io.IOException; +import java.nio.file.Path; + +import org.eclipse.aether.spi.io.ChecksumProcessor; + +/** + * A simple file processor implementation to help satisfy component requirements during tests. + */ +public class TestChecksumProcessor implements ChecksumProcessor { + private final TestFileProcessor testFileProcessor = new TestFileProcessor(); + + @Override + public String readChecksum(Path checksumFile) throws IOException { + return testFileProcessor.readChecksum(checksumFile.toFile()); + } + + @Override + public void writeChecksum(Path checksumFile, String checksum) throws IOException { + testFileProcessor.writeChecksum(checksumFile.toFile(), checksum); + } +} 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 new file mode 100644 index 000000000..0a263c883 --- /dev/null +++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestPathProcessor.java @@ -0,0 +1,59 @@ +/* + * 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.internal.test.util; + +import java.io.*; +import java.nio.file.Path; + +import org.eclipse.aether.spi.io.PathProcessor; + +/** + * A simple file processor implementation to help satisfy component requirements during tests. + */ +public class TestPathProcessor implements PathProcessor { + + private final TestFileProcessor testFileProcessor = new TestFileProcessor(); + + 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 void copy(Path source, Path target) throws IOException { + copy(source, target, null); + } + + 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()); + } +} 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 fea00f062..4ca9707f5 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 @@ -18,7 +18,6 @@ */ package org.eclipse.aether.transport.apache; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; @@ -30,6 +29,7 @@ import java.net.UnknownHostException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.util.Collections; @@ -582,13 +582,13 @@ private T commonHeaders(T request) { } @SuppressWarnings("checkstyle:magicnumber") - private void resume(T request, GetTask task) { + private void resume(T request, GetTask task) throws IOException { long resumeOffset = task.getResumeOffset(); - if (resumeOffset > 0L && task.getDataFile() != null) { + if (resumeOffset > 0L && task.getDataPath() != null) { + long lastModified = Files.getLastModifiedTime(task.getDataPath()).toMillis(); request.setHeader(HttpHeaders.RANGE, "bytes=" + resumeOffset + '-'); request.setHeader( - HttpHeaders.IF_UNMODIFIED_SINCE, - DateUtils.formatDate(new Date(task.getDataFile().lastModified() - 60L * 1000L))); + HttpHeaders.IF_UNMODIFIED_SINCE, DateUtils.formatDate(new Date(lastModified - 60L * 1000L))); request.setHeader(HttpHeaders.ACCEPT_ENCODING, "identity"); } } @@ -644,17 +644,17 @@ public void handle(CloseableHttpResponse response) throws IOException, TransferC } final boolean resume = offset > 0L; - final File dataFile = task.getDataFile(); + final Path dataFile = task.getDataPath(); if (dataFile == null) { try (InputStream is = entity.getContent()) { utilGet(task, is, true, length, resume); extractChecksums(response); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile.toPath())) { - task.setDataFile(tempFile.getPath().toFile(), resume); - if (resume && Files.isRegularFile(dataFile.toPath())) { - try (InputStream inputStream = Files.newInputStream(dataFile.toPath())) { + try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + task.setDataPath(tempFile.getPath(), resume); + if (resume && Files.isRegularFile(dataFile)) { + try (InputStream inputStream = Files.newInputStream(dataFile)) { Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING); } } @@ -663,17 +663,16 @@ public void handle(CloseableHttpResponse response) throws IOException, TransferC } tempFile.move(); } finally { - task.setDataFile(dataFile); + task.setDataPath(dataFile); } } - if (task.getDataFile() != null) { + if (task.getDataPath() != null) { Header lastModifiedHeader = response.getFirstHeader(HttpHeaders.LAST_MODIFIED); // note: Wagon also does first not last if (lastModifiedHeader != null) { Date lastModified = DateUtils.parseDate(lastModifiedHeader.getValue()); if (lastModified != null) { - Files.setLastModifiedTime( - task.getDataFile().toPath(), FileTime.fromMillis(lastModified.getTime())); + Files.setLastModifiedTime(task.getDataPath(), FileTime.fromMillis(lastModified.getTime())); } } } diff --git a/maven-resolver-transport-file/pom.xml b/maven-resolver-transport-file/pom.xml index 1f19bdf5d..8813871bd 100644 --- a/maven-resolver-transport-file/pom.xml +++ b/maven-resolver-transport-file/pom.xml @@ -61,6 +61,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-params + test + org.apache.maven.resolver maven-resolver-test-util @@ -71,6 +76,11 @@ slf4j-simple test + + com.google.jimfs + jimfs + test + diff --git a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java b/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java index e2cde760d..eb378e113 100644 --- a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java +++ b/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java @@ -18,39 +18,37 @@ */ package org.eclipse.aether.transport.file; -import java.io.File; -import java.nio.file.Files; +import java.nio.file.*; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryUriUtils; import org.eclipse.aether.spi.connector.transport.AbstractTransporter; import org.eclipse.aether.spi.connector.transport.GetTask; import org.eclipse.aether.spi.connector.transport.PeekTask; import org.eclipse.aether.spi.connector.transport.PutTask; import org.eclipse.aether.spi.connector.transport.TransportTask; import org.eclipse.aether.transfer.NoTransporterException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A transporter using {@link java.io.File}. */ final class FileTransporter extends AbstractTransporter { - private static final Logger LOGGER = LoggerFactory.getLogger(FileTransporter.class); - - private final File basedir; + private final Path basePath; FileTransporter(RemoteRepository repository) throws NoTransporterException { - if (!"file".equalsIgnoreCase(repository.getProtocol())) { - throw new NoTransporterException(repository); + try { + basePath = Paths.get(RepositoryUriUtils.toUri(repository.getUrl())).toAbsolutePath(); + } catch (FileSystemNotFoundException | IllegalArgumentException e) { + throw new NoTransporterException(repository, e); } - basedir = new File(PathUtils.basedir(repository.getUrl())).getAbsoluteFile(); } - File getBasedir() { - return basedir; + Path getBasePath() { + return basePath; } + @Override public int classify(Throwable error) { if (error instanceof ResourceNotFoundException) { return ERROR_NOT_FOUND; @@ -60,36 +58,34 @@ public int classify(Throwable error) { @Override protected void implPeek(PeekTask task) throws Exception { - getFile(task, true); + getPath(task, true); } @Override protected void implGet(GetTask task) throws Exception { - File file = getFile(task, true); - utilGet(task, Files.newInputStream(file.toPath()), true, file.length(), false); + Path path = getPath(task, true); + utilGet(task, Files.newInputStream(path), true, Files.size(path), false); } @Override protected void implPut(PutTask task) throws Exception { - File file = getFile(task, false); - file.getParentFile().mkdirs(); + Path path = getPath(task, false); + Files.createDirectories(path.getParent()); try { - utilPut(task, Files.newOutputStream(file.toPath()), true); + utilPut(task, Files.newOutputStream(path), true); } catch (Exception e) { - if (!file.delete() && file.exists()) { - LOGGER.debug("Could not delete partial file {}", file); - } + Files.deleteIfExists(path); throw e; } } - private File getFile(TransportTask task, boolean required) throws Exception { + private Path getPath(TransportTask task, boolean required) throws Exception { String path = task.getLocation().getPath(); if (path.contains("../")) { throw new IllegalArgumentException("illegal resource path: " + path); } - File file = new File(basedir, path); - if (required && !file.exists()) { + Path file = basePath.resolve(path); + if (required && !Files.isRegularFile(file)) { throw new ResourceNotFoundException("Could not locate " + file); } return file; diff --git a/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java b/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java index 87c58028d..a6455fe21 100644 --- a/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java +++ b/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java @@ -18,11 +18,16 @@ */ package org.eclipse.aether.transport.file; -import java.io.File; import java.io.FileNotFoundException; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.internal.test.util.TestFileUtils; import org.eclipse.aether.internal.test.util.TestUtils; @@ -35,8 +40,10 @@ import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.transfer.TransferCancelledException; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import static org.junit.jupiter.api.Assertions.*; @@ -50,7 +57,14 @@ public class FileTransporterTest { private Transporter transporter; - private File repoDir; + private Path repoDir; + + private FileSystem fileSystem; + + enum FS { + DEFAULT, + JIMFS + } private RemoteRepository newRepo(String url) { return new RemoteRepository.Builder("test", "default", url).build(); @@ -61,43 +75,60 @@ private void newTransporter(String url) throws Exception { transporter.close(); transporter = null; } + if (factory == null) { + factory = new FileTransporterFactory(); + } + if (session == null) { + session = TestUtils.newSession(); + } transporter = factory.newInstance(session, newRepo(url)); } - @BeforeEach - void setUp() throws Exception { - session = TestUtils.newSession(); - factory = new FileTransporterFactory(); - repoDir = TestFileUtils.createTempDir(); - TestFileUtils.writeString(new File(repoDir, "file.txt"), "test"); - TestFileUtils.writeString(new File(repoDir, "empty.txt"), ""); - TestFileUtils.writeString(new File(repoDir, "some space.txt"), "space"); - newTransporter(repoDir.toURI().toString()); + void setUp(FS fs) { + try { + fileSystem = fs == FS.JIMFS ? Jimfs.newFileSystem() : null; + repoDir = fileSystem == null ? TestFileUtils.createTempDir().toPath() : fileSystem.getPath("."); + Files.write(repoDir.resolve("file.txt"), "test".getBytes(StandardCharsets.UTF_8)); + Files.write(repoDir.resolve("empty.txt"), "".getBytes(StandardCharsets.UTF_8)); + Files.write(repoDir.resolve("some space.txt"), "space".getBytes(StandardCharsets.UTF_8)); + newTransporter(repoDir.toUri().toASCIIString()); + } catch (Exception e) { + Assertions.fail(e); + } } @AfterEach - void tearDown() { + void tearDown() throws Exception { if (transporter != null) { transporter.close(); transporter = null; } + if (fileSystem != null) { + fileSystem.close(); + } factory = null; session = null; } - @Test - void testClassify() { + @ParameterizedTest + @EnumSource(FS.class) + void testClassify(FS fs) { + setUp(fs); assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException())); assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new ResourceNotFoundException("test"))); } - @Test - void testPeek() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPeek(FS fs) throws Exception { + setUp(fs); transporter.peek(new PeekTask(URI.create("file.txt"))); } - @Test - void testPeek_NotFound() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPeek_NotFound(FS fs) throws Exception { + setUp(fs); try { transporter.peek(new PeekTask(URI.create("missing.txt"))); fail("Expected error"); @@ -106,8 +137,10 @@ void testPeek_NotFound() throws Exception { } } - @Test - void testPeek_Closed() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPeek_Closed(FS fs) throws Exception { + setUp(fs); transporter.close(); try { transporter.peek(new PeekTask(URI.create("missing.txt"))); @@ -117,8 +150,10 @@ void testPeek_Closed() throws Exception { } } - @Test - void testGet_ToMemory() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_ToMemory(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); GetTask task = new GetTask(URI.create("file.txt")).setListener(listener); transporter.get(task); @@ -130,13 +165,15 @@ void testGet_ToMemory() throws Exception { assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8)); } - @Test - void testGet_ToFile() throws Exception { - File file = TestFileUtils.createTempFile("failure"); + @ParameterizedTest + @EnumSource(FS.class) + void testGet_ToFile(FS fs) throws Exception { + setUp(fs); + Path file = TestFileUtils.createTempFile("failure").toPath(); RecordingTransportListener listener = new RecordingTransportListener(); - GetTask task = new GetTask(URI.create("file.txt")).setDataFile(file).setListener(listener); + GetTask task = new GetTask(URI.create("file.txt")).setDataPath(file).setListener(listener); transporter.get(task); - assertEquals("test", TestFileUtils.readString(file)); + assertEquals("test", TestFileUtils.readString(file.toFile())); assertEquals(0L, listener.dataOffset); assertEquals(4L, listener.dataLength); assertEquals(1, listener.startedCount); @@ -144,13 +181,15 @@ void testGet_ToFile() throws Exception { assertEquals("test", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8)); } - @Test - void testGet_EmptyResource() throws Exception { - File file = TestFileUtils.createTempFile("failure"); + @ParameterizedTest + @EnumSource(FS.class) + void testGet_EmptyResource(FS fs) throws Exception { + setUp(fs); + Path file = TestFileUtils.createTempFile("failure").toPath(); RecordingTransportListener listener = new RecordingTransportListener(); - GetTask task = new GetTask(URI.create("empty.txt")).setDataFile(file).setListener(listener); + GetTask task = new GetTask(URI.create("empty.txt")).setDataPath(file).setListener(listener); transporter.get(task); - assertEquals("", TestFileUtils.readString(file)); + assertEquals("", TestFileUtils.readString(file.toFile())); assertEquals(0L, listener.dataOffset); assertEquals(0L, listener.dataLength); assertEquals(1, listener.startedCount); @@ -158,38 +197,48 @@ void testGet_EmptyResource() throws Exception { assertEquals("", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8)); } - @Test - void testGet_EncodedResourcePath() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_EncodedResourcePath(FS fs) throws Exception { + setUp(fs); GetTask task = new GetTask(URI.create("some%20space.txt")); transporter.get(task); assertEquals("space", task.getDataString()); } - @Test - void testGet_Fragment() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_Fragment(FS fs) throws Exception { + setUp(fs); GetTask task = new GetTask(URI.create("file.txt#ignored")); transporter.get(task); assertEquals("test", task.getDataString()); } - @Test - void testGet_Query() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_Query(FS fs) throws Exception { + setUp(fs); GetTask task = new GetTask(URI.create("file.txt?ignored")); transporter.get(task); assertEquals("test", task.getDataString()); } - @Test - void testGet_FileHandleLeak() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_FileHandleLeak(FS fs) throws Exception { + setUp(fs); for (int i = 0; i < 100; i++) { - File file = TestFileUtils.createTempFile("failure"); - transporter.get(new GetTask(URI.create("file.txt")).setDataFile(file)); - assertTrue(file.delete(), i + ", " + file.getAbsolutePath()); + Path file = TestFileUtils.createTempFile("failure").toPath(); + transporter.get(new GetTask(URI.create("file.txt")).setDataPath(file)); + assertTrue(file.toFile().delete(), i + ", " + file.toFile().getAbsolutePath()); } } - @Test - void testGet_NotFound() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_NotFound(FS fs) throws Exception { + setUp(fs); try { transporter.get(new GetTask(URI.create("missing.txt"))); fail("Expected error"); @@ -198,8 +247,10 @@ void testGet_NotFound() throws Exception { } } - @Test - void testGet_Closed() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_Closed(FS fs) throws Exception { + setUp(fs); transporter.close(); try { transporter.get(new GetTask(URI.create("file.txt"))); @@ -209,8 +260,10 @@ void testGet_Closed() throws Exception { } } - @Test - void testGet_StartCancelled() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_StartCancelled(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); listener.cancelStart = true; GetTask task = new GetTask(URI.create("file.txt")).setListener(listener); @@ -226,8 +279,10 @@ void testGet_StartCancelled() throws Exception { assertEquals(0, listener.progressedCount); } - @Test - void testGet_ProgressCancelled() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testGet_ProgressCancelled(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); listener.cancelProgress = true; GetTask task = new GetTask(URI.create("file.txt")).setListener(listener); @@ -243,8 +298,10 @@ void testGet_ProgressCancelled() throws Exception { assertEquals(1, listener.progressedCount); } - @Test - void testPut_FromMemory() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_FromMemory(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload"); transporter.put(task); @@ -252,24 +309,28 @@ void testPut_FromMemory() throws Exception { assertEquals(6L, listener.dataLength); assertEquals(1, listener.startedCount); assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount); - assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); + assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8)); } - @Test - void testPut_FromFile() throws Exception { - File file = TestFileUtils.createTempFile("upload"); + @ParameterizedTest + @EnumSource(FS.class) + void testPut_FromFile(FS fs) throws Exception { + setUp(fs); + Path file = TestFileUtils.createTempFile("upload").toPath(); RecordingTransportListener listener = new RecordingTransportListener(); - PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataFile(file); + PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataPath(file); transporter.put(task); assertEquals(0L, listener.dataOffset); assertEquals(6L, listener.dataLength); assertEquals(1, listener.startedCount); assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount); - assertEquals("upload", TestFileUtils.readString(new File(repoDir, "file.txt"))); + assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8)); } - @Test - void testPut_EmptyResource() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_EmptyResource(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); PutTask task = new PutTask(URI.create("file.txt")).setListener(listener); transporter.put(task); @@ -277,11 +338,13 @@ void testPut_EmptyResource() throws Exception { assertEquals(0L, listener.dataLength); assertEquals(1, listener.startedCount); assertEquals(0, listener.progressedCount); - assertEquals("", TestFileUtils.readString(new File(repoDir, "file.txt"))); + assertEquals("", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8)); } - @Test - void testPut_NonExistentParentDir() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_NonExistentParentDir(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); PutTask task = new PutTask(URI.create("dir/sub/dir/file.txt")) .setListener(listener) @@ -291,11 +354,15 @@ void testPut_NonExistentParentDir() throws Exception { assertEquals(6L, listener.dataLength); assertEquals(1, listener.startedCount); assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount); - assertEquals("upload", TestFileUtils.readString(new File(repoDir, "dir/sub/dir/file.txt"))); + assertEquals( + "upload", + new String(Files.readAllBytes(repoDir.resolve("dir/sub/dir/file.txt")), StandardCharsets.UTF_8)); } - @Test - void testPut_EncodedResourcePath() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_EncodedResourcePath(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); PutTask task = new PutTask(URI.create("some%20space.txt")) .setListener(listener) @@ -305,22 +372,26 @@ void testPut_EncodedResourcePath() throws Exception { assertEquals(2L, listener.dataLength); assertEquals(1, listener.startedCount); assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount); - assertEquals("OK", TestFileUtils.readString(new File(repoDir, "some space.txt"))); + assertEquals("OK", new String(Files.readAllBytes(repoDir.resolve("some space.txt")), StandardCharsets.UTF_8)); } - @Test - void testPut_FileHandleLeak() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_FileHandleLeak(FS fs) throws Exception { + setUp(fs); for (int i = 0; i < 100; i++) { - File src = TestFileUtils.createTempFile("upload"); - File dst = new File(repoDir, "file.txt"); - transporter.put(new PutTask(URI.create("file.txt")).setDataFile(src)); - assertTrue(src.delete(), i + ", " + src.getAbsolutePath()); - assertTrue(dst.delete(), i + ", " + dst.getAbsolutePath()); + Path src = TestFileUtils.createTempFile("upload").toPath(); + Path dst = repoDir.resolve("file.txt"); + transporter.put(new PutTask(URI.create("file.txt")).setDataPath(src)); + assertTrue(src.toFile().delete(), i + ", " + src.toFile().getAbsolutePath()); + assertTrue(Files.deleteIfExists(dst), i + ", " + dst.toAbsolutePath()); } } - @Test - void testPut_Closed() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_Closed(FS fs) throws Exception { + setUp(fs); transporter.close(); try { transporter.put(new PutTask(URI.create("missing.txt"))); @@ -330,8 +401,10 @@ void testPut_Closed() throws Exception { } } - @Test - void testPut_StartCancelled() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_StartCancelled(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); listener.cancelStart = true; PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload"); @@ -345,11 +418,13 @@ void testPut_StartCancelled() throws Exception { assertEquals(6L, listener.dataLength); assertEquals(1, listener.startedCount); assertEquals(0, listener.progressedCount); - assertFalse(new File(repoDir, "file.txt").exists()); + assertFalse(Files.exists(repoDir.resolve("file.txt"))); } - @Test - void testPut_ProgressCancelled() throws Exception { + @ParameterizedTest + @EnumSource(FS.class) + void testPut_ProgressCancelled(FS fs) throws Exception { + setUp(fs); RecordingTransportListener listener = new RecordingTransportListener(); listener.cancelProgress = true; PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload"); @@ -363,7 +438,7 @@ void testPut_ProgressCancelled() throws Exception { assertEquals(6L, listener.dataLength); assertEquals(1, listener.startedCount); assertEquals(1, listener.progressedCount); - assertFalse(new File(repoDir, "file.txt").exists()); + assertFalse(Files.exists(repoDir.resolve("file.txt"))); } @Test @@ -433,9 +508,33 @@ void testInit_HierarchicalUrlHostPath() throws Exception { testInit("file://host/dir", "/dir"); } + @Test + void testInit_NonDefaultFileSystemRelative() throws Exception { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path path = fs.getPath("dir"); + testInit(path.toUri().toASCIIString(), "/work/dir"); + } + } + + @Test + void testInit_NonDefaultFileSystemAbsolute() throws Exception { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path path = fs.getPath("/dir"); + testInit(path.toUri().toASCIIString(), "/dir"); + } + } + private void testInit(String base, String expected) throws Exception { newTransporter(base); - File exp = new File(expected).getAbsoluteFile(); - assertEquals(exp, ((FileTransporter) transporter).getBasedir()); + String exp = expected; + if (base.startsWith("file:")) { + // on def FileSystem we do extra dance that we do NOT do in case of non-default File Systems: + // like accepting weird URLs/URIs and resolving/abs against CWD, that may not be defined in case + // of non-default FileSystems (OTOH, they MAY do it, like JIMFS has $cwd="/work") + exp = Paths.get(expected).toAbsolutePath().toString(); + } + // compare path string representation only, as otherwise (Object equality) it would fail + // if we end up with non default FS for example + assertEquals(exp, ((FileTransporter) transporter).getBasePath().toString()); } } 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 29b4e24a6..77a2312d0 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 @@ -20,7 +20,7 @@ import javax.net.ssl.*; -import java.io.File; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -30,6 +30,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.security.cert.X509Certificate; @@ -60,6 +61,7 @@ import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; import org.eclipse.aether.spi.connector.transport.http.HttpTransporter; import org.eclipse.aether.spi.connector.transport.http.HttpTransporterException; +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; @@ -86,6 +88,8 @@ final class JdkTransporter extends AbstractTransporter implements HttpTransporte private final ChecksumExtractor checksumExtractor; + private final PathProcessor pathProcessor; + private final URI baseUri; private final HttpClient client; @@ -102,9 +106,11 @@ final class JdkTransporter extends AbstractTransporter implements HttpTransporte RepositorySystemSession session, RemoteRepository repository, int javaVersion, - ChecksumExtractor checksumExtractor) + ChecksumExtractor checksumExtractor, + PathProcessor pathProcessor) throws NoTransporterException { this.checksumExtractor = checksumExtractor; + this.pathProcessor = pathProcessor; try { URI uri = new URI(repository.getUrl()).parseServerAuthority(); if (uri.isOpaque()) { @@ -214,7 +220,7 @@ protected void implPeek(PeekTask task) throws Exception { @Override protected void implGet(GetTask task) throws Exception { - boolean resume = task.getResumeOffset() > 0L && task.getDataFile() != null; + boolean resume = task.getResumeOffset() > 0L && task.getDataPath() != null; HttpResponse response = null; try { @@ -227,11 +233,11 @@ protected void implGet(GetTask task) throws Exception { if (resume) { long resumeOffset = task.getResumeOffset(); + long lastModified = pathProcessor.lastModified(task.getDataPath(), 0L); request.header(RANGE, "bytes=" + resumeOffset + '-'); request.header( IF_UNMODIFIED_SINCE, - RFC7231.format( - Instant.ofEpochMilli(task.getDataFile().lastModified() - MODIFICATION_THRESHOLD))); + RFC7231.format(Instant.ofEpochMilli(lastModified - MODIFICATION_THRESHOLD))); request.header(ACCEPT_ENCODING, "identity"); } @@ -271,16 +277,16 @@ protected void implGet(GetTask task) throws Exception { } final boolean downloadResumed = offset > 0L; - final File dataFile = task.getDataFile(); + final Path dataFile = task.getDataPath(); if (dataFile == null) { try (InputStream is = response.body()) { utilGet(task, is, true, length, downloadResumed); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile.toPath())) { - task.setDataFile(tempFile.getPath().toFile(), downloadResumed); - if (downloadResumed && Files.isRegularFile(dataFile.toPath())) { - try (InputStream inputStream = Files.newInputStream(dataFile.toPath())) { + try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + task.setDataPath(tempFile.getPath(), downloadResumed); + if (downloadResumed && Files.isRegularFile(dataFile)) { + try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(dataFile))) { Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING); } } @@ -289,17 +295,17 @@ protected void implGet(GetTask task) throws Exception { } tempFile.move(); } finally { - task.setDataFile(dataFile); + task.setDataPath(dataFile); } } - if (task.getDataFile() != null) { + if (task.getDataPath() != null) { String lastModifiedHeader = response.headers() .firstValue(LAST_MODIFIED) .orElse(null); // note: Wagon also does first not last if (lastModifiedHeader != null) { try { Files.setLastModifiedTime( - task.getDataFile().toPath(), + task.getDataPath(), FileTime.fromMillis(ZonedDateTime.parse(lastModifiedHeader, RFC7231) .toInstant() .toEpochMilli())); diff --git a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java index 2d048a799..a91044ec5 100644 --- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java +++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java @@ -26,6 +26,7 @@ import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; import org.eclipse.aether.spi.connector.transport.http.HttpTransporter; import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import static java.util.Objects.requireNonNull; @@ -43,9 +44,12 @@ public final class JdkTransporterFactory implements HttpTransporterFactory { private final ChecksumExtractor checksumExtractor; + private final PathProcessor pathProcessor; + @Inject - public JdkTransporterFactory(ChecksumExtractor checksumExtractor) { + public JdkTransporterFactory(ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) { this.checksumExtractor = requireNonNull(checksumExtractor, "checksumExtractor"); + this.pathProcessor = requireNonNull(pathProcessor, "pathProcessor"); } @Override @@ -68,7 +72,7 @@ public HttpTransporter newInstance(RepositorySystemSession session, RemoteReposi throw new NoTransporterException(repository, "Only HTTP/HTTPS is supported"); } - return new JdkTransporter(session, repository, javaVersion(), checksumExtractor); + return new JdkTransporter(session, repository, javaVersion(), checksumExtractor, pathProcessor); } private static int javaVersion() { diff --git a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java index 3dc2eaed6..1a15aaed9 100644 --- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java +++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java @@ -21,6 +21,7 @@ import java.net.ConnectException; import java.net.URI; +import org.eclipse.aether.internal.impl.DefaultPathProcessor; import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.internal.test.util.http.HttpTransporterTest; import org.eclipse.aether.repository.RemoteRepository; @@ -84,14 +85,14 @@ protected void testRetryHandler_explicitCount_positive() {} protected void testPut_Authenticated_ExpectContinueRejected_ExplicitlyConfiguredHeader() {} public JdkTransporterTest() { - super(() -> new JdkTransporterFactory(standardChecksumExtractor())); + super(() -> new JdkTransporterFactory(standardChecksumExtractor(), new DefaultPathProcessor())); } @Test void enhanceConnectExceptionMessages() { String uri = "https://localhost:12345/"; RemoteRepository remoteRepository = new RemoteRepository.Builder("central", "default", uri).build(); - JdkTransporterFactory factory = new JdkTransporterFactory(s -> null); + JdkTransporterFactory factory = new JdkTransporterFactory(s -> null, new DefaultPathProcessor()); try (Transporter transporter = factory.newInstance(TestUtils.newSession(), remoteRepository)) { transporter.peek(new PeekTask(URI.create("repo/file.txt"))); diff --git a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-8/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-8/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java index 2096ac313..dcea7ce8b 100644 --- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-8/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java +++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk-8/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporterFactory.java @@ -26,6 +26,7 @@ import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; import org.eclipse.aether.spi.connector.transport.http.HttpTransporter; import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory; +import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.transfer.NoTransporterException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +47,7 @@ public final class JdkTransporterFactory implements HttpTransporterFactory { private float priority = 10.0f; @Inject - public JdkTransporterFactory(ChecksumExtractor checksumExtractor) { + public JdkTransporterFactory(ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) { // this is to equalize all Java version constructors to be same, so Supplier could work across all versions } 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 6050d8848..107dda514 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 @@ -20,12 +20,12 @@ import javax.net.ssl.*; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.security.cert.X509Certificate; @@ -205,7 +205,7 @@ protected void implPeek(PeekTask task) throws Exception { @Override protected void implGet(GetTask task) throws Exception { - boolean resume = task.getResumeOffset() > 0L && task.getDataFile() != null; + boolean resume = task.getResumeOffset() > 0L && task.getDataPath() != null; Response response; InputStreamResponseListener listener; @@ -225,9 +225,11 @@ protected void implGet(GetTask task) throws Exception { if (resume) { long resumeOffset = task.getResumeOffset(); + long lastModified = + Files.getLastModifiedTime(task.getDataPath()).toMillis(); request.headers(h -> { h.add(RANGE, "bytes=" + resumeOffset + '-'); - h.addDateField(IF_UNMODIFIED_SINCE, task.getDataFile().lastModified() - MODIFICATION_THRESHOLD); + h.addDateField(IF_UNMODIFIED_SINCE, lastModified - MODIFICATION_THRESHOLD); h.remove(HttpHeader.ACCEPT_ENCODING); h.add(ACCEPT_ENCODING, "identity"); }); @@ -273,16 +275,16 @@ protected void implGet(GetTask task) throws Exception { } final boolean downloadResumed = offset > 0L; - final File dataFile = task.getDataFile(); + final Path dataFile = task.getDataPath(); if (dataFile == null) { try (InputStream is = listener.getInputStream()) { utilGet(task, is, true, length, downloadResumed); } } else { - try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile.toPath())) { - task.setDataFile(tempFile.getPath().toFile(), downloadResumed); - if (downloadResumed && Files.isRegularFile(dataFile.toPath())) { - try (InputStream inputStream = Files.newInputStream(dataFile.toPath())) { + try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile)) { + task.setDataPath(tempFile.getPath(), downloadResumed); + if (downloadResumed && Files.isRegularFile(dataFile)) { + try (InputStream inputStream = Files.newInputStream(dataFile)) { Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING); } } @@ -291,15 +293,15 @@ protected void implGet(GetTask task) throws Exception { } tempFile.move(); } finally { - task.setDataFile(dataFile); + task.setDataPath(dataFile); } } - if (task.getDataFile() != null && response.getHeaders().getDateField(LAST_MODIFIED) != -1) { + if (task.getDataPath() != null && response.getHeaders().getDateField(LAST_MODIFIED) != -1) { long lastModified = response.getHeaders().getDateField(LAST_MODIFIED); // note: Wagon also does first not last if (lastModified != -1) { try { - Files.setLastModifiedTime(task.getDataFile().toPath(), FileTime.fromMillis(lastModified)); + Files.setLastModifiedTime(task.getDataPath(), FileTime.fromMillis(lastModified)); } catch (DateTimeParseException e) { // fall through } diff --git a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/PutTaskRequestContent.java b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/PutTaskRequestContent.java index 889187ff8..05af716bd 100644 --- a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/PutTaskRequestContent.java +++ b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/PutTaskRequestContent.java @@ -94,8 +94,8 @@ protected boolean produceContent(Producer producer) throws IOException { ByteBuffer buffer; boolean last; if (channel == null) { - if (putTask.getDataFile() != null) { - channel = Files.newByteChannel(putTask.getDataFile().toPath(), StandardOpenOption.READ); + if (putTask.getDataPath() != null) { + channel = Files.newByteChannel(putTask.getDataPath(), StandardOpenOption.READ); } else { channel = Channels.newChannel(putTask.newInputStream()); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/DirectoryUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/DirectoryUtils.java index 244c7bd34..8a13e742d 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/DirectoryUtils.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/DirectoryUtils.java @@ -24,6 +24,7 @@ import java.nio.file.Paths; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.LocalRepository; import static java.util.Objects.requireNonNull; @@ -67,7 +68,7 @@ public static Path resolveDirectory(String name, Path base, boolean mayCreate) t if (namePath.isAbsolute()) { result = namePath.normalize(); } else { - result = base.resolve(namePath).normalize(); + result = base.resolve(name).normalize(); } if (!Files.exists(result)) { @@ -84,7 +85,7 @@ public static Path resolveDirectory(String name, Path base, boolean mayCreate) t * Creates {@link Path} instance out of session configuration, and (if relative) resolve it against local * repository basedir. Pre-populates values and invokes {@link #resolveDirectory(String, Path, boolean)}. *

- * For this method to work, {@link org.eclipse.aether.repository.LocalRepository#getBasedir()} must return + * For this method to work, {@link LocalRepository#getBasePath()} must return * non-{@code null} value, otherwise {@link NullPointerException} is thrown. * * @param session The session, may not be {@code null}. @@ -100,10 +101,10 @@ public static Path resolveDirectory( requireNonNull(session, "session is null"); requireNonNull(defaultName, "defaultName is null"); requireNonNull(nameKey, "nameKey is null"); - requireNonNull(session.getLocalRepository().getBasedir(), "session.localRepository.basedir is null"); + requireNonNull(session.getLocalRepository().getBasePath(), "session.localRepository.basePath is null"); return resolveDirectory( ConfigUtils.getString(session, defaultName, nameKey), - session.getLocalRepository().getBasedir().toPath(), + session.getLocalRepository().getBasePath(), mayCreate); } } 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 0a430f89f..fe0768efa 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 @@ -132,7 +132,7 @@ public void close() throws IOException { if (IS_WINDOWS) { copy(tempFile, file); } else { - Files.move(tempFile, file, StandardCopyOption.ATOMIC_MOVE); + Files.move(tempFile, file, StandardCopyOption.REPLACE_EXISTING); } } Files.deleteIfExists(tempFile); diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/DelegatingArtifact.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/DelegatingArtifact.java index ef48e8b50..256c640d2 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/DelegatingArtifact.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/DelegatingArtifact.java @@ -19,6 +19,7 @@ package org.eclipse.aether.util.artifact; import java.io.File; +import java.nio.file.Path; import java.util.Map; import org.eclipse.aether.artifact.AbstractArtifact; @@ -52,18 +53,22 @@ protected DelegatingArtifact(Artifact delegate) { */ protected abstract DelegatingArtifact newInstance(Artifact delegate); + @Override public String getGroupId() { return delegate.getGroupId(); } + @Override public String getArtifactId() { return delegate.getArtifactId(); } + @Override public String getVersion() { return delegate.getVersion(); } + @Override public Artifact setVersion(String version) { Artifact artifact = delegate.setVersion(version); if (artifact != delegate) { @@ -72,26 +77,39 @@ public Artifact setVersion(String version) { return this; } + @Override public String getBaseVersion() { return delegate.getBaseVersion(); } + @Override public boolean isSnapshot() { return delegate.isSnapshot(); } + @Override public String getClassifier() { return delegate.getClassifier(); } + @Override public String getExtension() { return delegate.getExtension(); } + @Deprecated + @Override public File getFile() { return delegate.getFile(); } + @Override + public Path getPath() { + return delegate.getPath(); + } + + @Deprecated + @Override public Artifact setFile(File file) { Artifact artifact = delegate.setFile(file); if (artifact != delegate) { @@ -100,6 +118,15 @@ public Artifact setFile(File file) { return this; } + @Override + public Artifact setPath(Path path) { + Artifact artifact = delegate.setPath(path); + if (artifact != delegate) { + return newInstance(artifact); + } + return this; + } + public String getProperty(String key, String defaultValue) { return delegate.getProperty(key, defaultValue); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SubArtifact.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SubArtifact.java index 1e3282ad8..5e8c677e7 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SubArtifact.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SubArtifact.java @@ -19,6 +19,7 @@ package org.eclipse.aether.util.artifact; import java.io.File; +import java.nio.file.Path; import java.util.Map; import java.util.Objects; @@ -39,7 +40,7 @@ public final class SubArtifact extends AbstractArtifact { private final String extension; - private final File file; + private final Path path; private final Map properties; @@ -72,6 +73,22 @@ public SubArtifact(Artifact mainArtifact, String classifier, String extension, F this(mainArtifact, classifier, extension, null, file); } + /** + * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk + * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier + * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be + * used to refer to the GPG signature of an artifact. + * + * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. + * @param classifier The classifier for this artifact, may be {@code null} if none. + * @param extension The extension for this artifact, may be {@code null} if none. + * @param path The file for this artifact, may be {@code null} if unresolved. + * @since 2.0.0 + */ + public SubArtifact(Artifact mainArtifact, String classifier, String extension, Path path) { + this(mainArtifact, classifier, extension, null, path); + } + /** * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier @@ -84,7 +101,7 @@ public SubArtifact(Artifact mainArtifact, String classifier, String extension, F * @param properties The properties of the artifact, may be {@code null}. */ public SubArtifact(Artifact mainArtifact, String classifier, String extension, Map properties) { - this(mainArtifact, classifier, extension, properties, null); + this(mainArtifact, classifier, extension, properties, (Path) null); } /** @@ -101,71 +118,112 @@ public SubArtifact(Artifact mainArtifact, String classifier, String extension, M */ public SubArtifact( Artifact mainArtifact, String classifier, String extension, Map properties, File file) { + this(mainArtifact, classifier, extension, properties, file != null ? file.toPath() : null); + } + + /** + * Creates a new sub artifact. The classifier and extension specified for this artifact may use the asterisk + * character "*" to refer to the corresponding property of the main artifact. For instance, the classifier + * "*-sources" can be used to refer to the source attachment of an artifact. Likewise, the extension "*.asc" can be + * used to refer to the GPG signature of an artifact. + * + * @param mainArtifact The artifact from which to derive the identity, must not be {@code null}. + * @param classifier The classifier for this artifact, may be {@code null} if none. + * @param extension The extension for this artifact, may be {@code null} if none. + * @param properties The properties of the artifact, may be {@code null}. + * @param path The file for this artifact, may be {@code null} if unresolved. + * @since 2.0.0 + */ + public SubArtifact( + Artifact mainArtifact, String classifier, String extension, Map properties, Path path) { this.mainArtifact = requireNonNull(mainArtifact, "main artifact cannot be null"); this.classifier = classifier; this.extension = extension; - this.file = file; + this.path = path; this.properties = copyProperties(properties); } private SubArtifact( - Artifact mainArtifact, String classifier, String extension, File file, Map properties) { + Artifact mainArtifact, String classifier, String extension, Path path, Map properties) { // NOTE: This constructor assumes immutability of the provided properties, for internal use only this.mainArtifact = mainArtifact; this.classifier = classifier; this.extension = extension; - this.file = file; + this.path = path; this.properties = properties; } + @Override public String getGroupId() { return mainArtifact.getGroupId(); } + @Override public String getArtifactId() { return mainArtifact.getArtifactId(); } + @Override public String getVersion() { return mainArtifact.getVersion(); } + @Override public String getBaseVersion() { return mainArtifact.getBaseVersion(); } + @Override public boolean isSnapshot() { return mainArtifact.isSnapshot(); } + @Override public String getClassifier() { return expand(classifier, mainArtifact.getClassifier()); } + @Override public String getExtension() { return expand(extension, mainArtifact.getExtension()); } + @Deprecated + @Override public File getFile() { - return file; + return path != null ? path.toFile() : null; } + @Override + public Path getPath() { + return path; + } + + @Deprecated + @Override public Artifact setFile(File file) { - if (Objects.equals(this.file, file)) { + return setPath(file != null ? file.toPath() : null); + } + + @Override + public Artifact setPath(Path path) { + if (Objects.equals(this.path, path)) { return this; } - return new SubArtifact(mainArtifact, classifier, extension, file, properties); + return new SubArtifact(mainArtifact, classifier, extension, path, properties); } + @Override public Map getProperties() { return properties; } + @Override public Artifact setProperties(Map properties) { if (this.properties.equals(properties) || (properties == null && this.properties.isEmpty())) { return this; } - return new SubArtifact(mainArtifact, classifier, extension, properties, file); + return new SubArtifact(mainArtifact, classifier, extension, properties, path); } private static String expand(String pattern, String replacement) { diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/NodeListGenerator.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/NodeListGenerator.java index 44d45831f..68b61d739 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/NodeListGenerator.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/visitor/NodeListGenerator.java @@ -19,6 +19,7 @@ package org.eclipse.aether.util.graph.visitor; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -97,11 +98,23 @@ public List getArtifacts(boolean includeUnresolved) { * Gets the files of resolved artifacts seen during the graph traversal. * * @return The list of artifact files, never {@code null}. + * @deprecated Use {@link #getPaths()} instead. */ + @Deprecated public List getFiles() { return getFiles(getNodes()); } + /** + * Gets the files of resolved artifacts seen during the graph traversal. + * + * @return The list of artifact files, never {@code null}. + * @since 2.0.0 + */ + public List getPaths() { + return getPaths(getNodes()); + } + /** * Gets a class path by concatenating the artifact files of the visited dependency nodes. Nodes with unresolved * artifacts are automatically skipped. @@ -119,17 +132,18 @@ static List getNodesWithDependencies(List nodes) static List getDependencies(List nodes, boolean includeUnresolved) { return getNodesWithDependencies(nodes).stream() .map(DependencyNode::getDependency) - .filter(d -> includeUnresolved || d.getArtifact().getFile() != null) + .filter(d -> includeUnresolved || d.getArtifact().getPath() != null) .collect(toList()); } static List getArtifacts(List nodes, boolean includeUnresolved) { return getNodesWithDependencies(nodes).stream() .map(d -> d.getDependency().getArtifact()) - .filter(artifact -> includeUnresolved || artifact.getFile() != null) + .filter(artifact -> includeUnresolved || artifact.getPath() != null) .collect(toList()); } + @Deprecated static List getFiles(List nodes) { return getNodesWithDependencies(nodes).stream() .map(d -> d.getDependency().getArtifact().getFile()) @@ -137,7 +151,17 @@ static List getFiles(List nodes) { .collect(toList()); } + static List getPaths(List nodes) { + return getNodesWithDependencies(nodes).stream() + .map(d -> d.getDependency().getArtifact().getPath()) + .filter(Objects::nonNull) + .collect(toList()); + } + static String getClassPath(List nodes) { - return getFiles(nodes).stream().map(File::getAbsolutePath).collect(joining(File.pathSeparator)); + return getPaths(nodes).stream() + .map(Path::toAbsolutePath) + .map(Path::toString) + .collect(joining(File.pathSeparator)); } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/ChainedLocalRepositoryManager.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/ChainedLocalRepositoryManager.java index 90239559c..6b32ab2f4 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/ChainedLocalRepositoryManager.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/ChainedLocalRepositoryManager.java @@ -18,7 +18,6 @@ */ package org.eclipse.aether.util.repository; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -120,7 +119,7 @@ public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRe for (LocalRepositoryManager lrm : tail) { result = lrm.find(session, request); - if (result.getFile() != null) { + if (result.getPath() != null) { if (ignoreTailAvailability) { result.setAvailable(true); return result; @@ -141,7 +140,7 @@ public void add(RepositorySystemSession session, LocalArtifactRegistration reque artifactPath = getPathForLocalArtifact(request.getArtifact()); } - Path file = new File(head.getRepository().getBasedir(), artifactPath).toPath(); + Path file = head.getRepository().getBasePath().resolve(artifactPath); if (Files.isRegularFile(file)) { head.add(session, request); } @@ -150,13 +149,13 @@ public void add(RepositorySystemSession session, LocalArtifactRegistration reque @Override public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) { LocalMetadataResult result = head.find(session, request); - if (result.getFile() != null) { + if (result.getPath() != null) { return result; } for (LocalRepositoryManager lrm : tail) { result = lrm.find(session, request); - if (result.getFile() != null) { + if (result.getPath() != null) { return result; } } @@ -172,7 +171,7 @@ public void add(RepositorySystemSession session, LocalMetadataRegistration reque metadataPath = getPathForLocalMetadata(request.getMetadata()); } - Path file = new File(head.getRepository().getBasedir(), metadataPath).toPath(); + Path file = head.getRepository().getBasePath().resolve(metadataPath); if (Files.isRegularFile(file)) { head.add(session, request); } diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/artifact/SubArtifactTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/artifact/SubArtifactTest.java index f3bbe3dd0..c7fd3335c 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/artifact/SubArtifactTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/artifact/SubArtifactTest.java @@ -128,7 +128,7 @@ void testPropertiesCopied() { Map props = new HashMap<>(); props.put("key", "value1"); - Artifact a = new SubArtifact(newMainArtifact("gid:aid:ver"), "", "pom", props, null); + Artifact a = new SubArtifact(newMainArtifact("gid:aid:ver"), "", "pom", props, (File) null); assertEquals("value1", a.getProperty("key", null)); props.clear(); assertEquals("value1", a.getProperty("key", null)); diff --git a/pom.xml b/pom.xml index 51ac0164e..a9a9e37a3 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ 10.0.20 - 4.0.0-alpha-12 + 4.0.0-alpha-13-SNAPSHOT [3.8.8,) [21,) @@ -265,6 +265,12 @@ ${slf4jVersion} test + + + com.google.jimfs + jimfs + 1.3.0 +