001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.transfer;
020
021import java.io.File;
022import java.nio.file.Path;
023import java.time.Clock;
024import java.time.Instant;
025
026import org.eclipse.aether.RequestTrace;
027
028/**
029 * Describes a resource being uploaded or downloaded by the repository system.
030 */
031public final class TransferResource {
032
033    private static Clock clock = Clock.systemUTC();
034
035    private final String repositoryId;
036
037    private final String repositoryUrl;
038
039    private final String resourceName;
040
041    private final Object resource;
042
043    private final Path path;
044
045    private final Instant startTime;
046
047    private final RequestTrace trace;
048
049    private long contentLength = -1L;
050
051    private long resumeOffset;
052
053    public static Clock getClock() {
054        return clock;
055    }
056
057    public static void setClock(Clock clock) {
058        TransferResource.clock = clock;
059    }
060
061    /**
062     * Creates a new transfer resource with the specified properties.
063     *
064     * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
065     *                     empty if unknown.
066     * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
067     *            trailing slash will automatically be added if missing.
068     * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
069     *            (if any) will be automatically removed.
070     * @param file The source/target file involved in the transfer, may be {@code null}.
071     * @param trace The trace information, may be {@code null}.
072     *
073     * @since 1.1.0
074     * @deprecated Use {@link TransferResource(String, String, String, Path, Object, RequestTrace)} instead.
075     */
076    @Deprecated
077    public TransferResource(
078            String repositoryId, String repositoryUrl, String resourceName, File file, RequestTrace trace) {
079        this(repositoryId, repositoryUrl, resourceName, file != null ? file.toPath() : null, null, trace);
080    }
081
082    /**
083     * Creates a new transfer resource with the specified properties.
084     *
085     * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
086     *                     empty if unknown.
087     * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
088     *            trailing slash will automatically be added if missing.
089     * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
090     *            (if any) will be automatically removed.
091     * @param path The source/target file involved in the transfer, may be {@code null}.
092     * @param resource The representation of this resource, may be {@code null}.
093     * @param trace The trace information, may be {@code null}.
094     *
095     * @since 2.0.0
096     */
097    public TransferResource(
098            String repositoryId,
099            String repositoryUrl,
100            String resourceName,
101            Path path,
102            Object resource,
103            RequestTrace trace) {
104        if (repositoryId == null || repositoryId.isEmpty()) {
105            this.repositoryId = "";
106        } else {
107            this.repositoryId = repositoryId;
108        }
109
110        if (repositoryUrl == null || repositoryUrl.isEmpty()) {
111            this.repositoryUrl = "";
112        } else if (repositoryUrl.endsWith("/")) {
113            this.repositoryUrl = repositoryUrl;
114        } else {
115            this.repositoryUrl = repositoryUrl + '/';
116        }
117
118        if (resourceName == null || resourceName.isEmpty()) {
119            this.resourceName = "";
120        } else if (resourceName.startsWith("/")) {
121            this.resourceName = resourceName.substring(1);
122        } else {
123            this.resourceName = resourceName;
124        }
125
126        this.path = path;
127        this.resource = resource;
128        this.trace = trace;
129        this.startTime = getClock().instant();
130    }
131
132    /**
133     * The ID of the repository, e.g., "central".
134     *
135     * @return The ID of the repository or an empty string if unknown, never {@code null}.
136     *
137     * @since 1.1.0
138     */
139    public String getRepositoryId() {
140        return repositoryId;
141    }
142
143    /**
144     * The base URL of the repository, e.g. "https://repo1.maven.org/maven2/". Unless the URL is unknown, it will be
145     * terminated by a trailing slash.
146     *
147     * @return The base URL of the repository or an empty string if unknown, never {@code null}.
148     */
149    public String getRepositoryUrl() {
150        return repositoryUrl;
151    }
152
153    /**
154     * The path of the resource relative to the repository's base URL, e.g. "org/apache/maven/maven/3.0/maven-3.0.pom".
155     *
156     * @return The path of the resource, never {@code null}.
157     */
158    public String getResourceName() {
159        return resourceName;
160    }
161
162    /**
163     * The representation of "resource", if any. The content of this field may be
164     * {@link org.eclipse.aether.artifact.Artifact} or {@link org.eclipse.aether.metadata.Metadata} or {@code null}
165     * in case of some legacy flow. Preferred way to handle returned value is with {@code instanceof}.
166     *
167     * @return The representation of this resource, may be {@code null}.
168     * @since 2.0.0
169     */
170    public Object getResource() {
171        return resource;
172    }
173
174    /**
175     * Gets the local file being uploaded or downloaded. When the repository system merely checks for the existence of a
176     * remote resource, no local file will be involved in the transfer.
177     *
178     * @return The source/target file involved in the transfer or {@code null} if none.
179     * @deprecated Use {@link #getPath()} instead.
180     */
181    @Deprecated
182    public File getFile() {
183        return path != null ? path.toFile() : null;
184    }
185
186    /**
187     * Gets the local file being uploaded or downloaded. When the repository system merely checks for the existence of a
188     * remote resource, no local file will be involved in the transfer.
189     *
190     * @return The source/target file involved in the transfer or {@code null} if none.
191     * @since 2.0.0
192     */
193    public Path getPath() {
194        return path;
195    }
196
197    /**
198     * The size of the resource in bytes. Note that the size of a resource during downloads might be unknown to the
199     * client which is usually the case when transfers employ compression like gzip. In general, the content length is
200     * not known until the transfer has {@link TransferListener#transferStarted(TransferEvent) started}.
201     *
202     * @return The size of the resource in bytes or a negative value if unknown.
203     */
204    public long getContentLength() {
205        return contentLength;
206    }
207
208    /**
209     * Sets the size of the resource in bytes.
210     *
211     * @param contentLength The size of the resource in bytes or a negative value if unknown.
212     * @return This resource for chaining, never {@code null}.
213     */
214    public TransferResource setContentLength(long contentLength) {
215        this.contentLength = contentLength;
216        return this;
217    }
218
219    /**
220     * Gets the byte offset within the resource from which the download starts. A positive offset indicates a previous
221     * download attempt is being resumed, {@code 0} means the transfer starts at the first byte.
222     *
223     * @return The zero-based index of the first byte being transferred, never negative.
224     */
225    public long getResumeOffset() {
226        return resumeOffset;
227    }
228
229    /**
230     * Sets the byte offset within the resource at which the download starts.
231     *
232     * @param resumeOffset The zero-based index of the first byte being transferred, must not be negative.
233     * @return This resource for chaining, never {@code null}.
234     */
235    public TransferResource setResumeOffset(long resumeOffset) {
236        if (resumeOffset < 0L) {
237            throw new IllegalArgumentException("resume offset cannot be negative");
238        }
239        this.resumeOffset = resumeOffset;
240        return this;
241    }
242
243    /**
244     * Gets the timestamp when the transfer of this resource was started.
245     *
246     * @return The timestamp when the transfer of this resource was started.
247     * @deprecated use {@link #getStartTime()}
248     */
249    @Deprecated
250    public long getTransferStartTime() {
251        return startTime.toEpochMilli();
252    }
253
254    /**
255     * Gets the timestamp when the transfer of this resource was started.
256     *
257     * @return The timestamp when the transfer of this resource was started.
258     */
259    public Instant getStartTime() {
260        return startTime;
261    }
262
263    /**
264     * Gets the trace information that describes the higher level request/operation during which this resource is
265     * transferred.
266     *
267     * @return The trace information about the higher level operation or {@code null} if none.
268     */
269    public RequestTrace getTrace() {
270        return trace;
271    }
272
273    @Override
274    public String toString() {
275        return getRepositoryUrl() + getResourceName() + " <> " + getPath();
276    }
277}