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;
023
024import org.eclipse.aether.RequestTrace;
025
026/**
027 * Describes a resource being uploaded or downloaded by the repository system.
028 */
029public final class TransferResource {
030
031    private final String repositoryId;
032
033    private final String repositoryUrl;
034
035    private final String resourceName;
036
037    private final Object resource;
038
039    private final Path path;
040
041    private final long startTime;
042
043    private final RequestTrace trace;
044
045    private long contentLength = -1L;
046
047    private long resumeOffset;
048
049    /**
050     * Creates a new transfer resource with the specified properties.
051     *
052     * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
053     *                     empty if unknown.
054     * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
055     *            trailing slash will automatically be added if missing.
056     * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
057     *            (if any) will be automatically removed.
058     * @param file The source/target file involved in the transfer, may be {@code null}.
059     * @param trace The trace information, may be {@code null}.
060     *
061     * @since 1.1.0
062     * @deprecated Use {@link TransferResource(String, String, String, Path, Object, RequestTrace)} instead.
063     */
064    @Deprecated
065    public TransferResource(
066            String repositoryId, String repositoryUrl, String resourceName, File file, RequestTrace trace) {
067        this(repositoryId, repositoryUrl, resourceName, file != null ? file.toPath() : null, null, trace);
068    }
069
070    /**
071     * Creates a new transfer resource with the specified properties.
072     *
073     * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
074     *                     empty if unknown.
075     * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
076     *            trailing slash will automatically be added if missing.
077     * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
078     *            (if any) will be automatically removed.
079     * @param path The source/target file involved in the transfer, may be {@code null}.
080     * @param resource The representation of this resource, may be {@code null}.
081     * @param trace The trace information, may be {@code null}.
082     *
083     * @since 2.0.0
084     */
085    public TransferResource(
086            String repositoryId,
087            String repositoryUrl,
088            String resourceName,
089            Path path,
090            Object resource,
091            RequestTrace trace) {
092        if (repositoryId == null || repositoryId.isEmpty()) {
093            this.repositoryId = "";
094        } else {
095            this.repositoryId = repositoryId;
096        }
097
098        if (repositoryUrl == null || repositoryUrl.isEmpty()) {
099            this.repositoryUrl = "";
100        } else if (repositoryUrl.endsWith("/")) {
101            this.repositoryUrl = repositoryUrl;
102        } else {
103            this.repositoryUrl = repositoryUrl + '/';
104        }
105
106        if (resourceName == null || resourceName.isEmpty()) {
107            this.resourceName = "";
108        } else if (resourceName.startsWith("/")) {
109            this.resourceName = resourceName.substring(1);
110        } else {
111            this.resourceName = resourceName;
112        }
113
114        this.path = path;
115        this.resource = resource;
116        this.trace = trace;
117        this.startTime = System.currentTimeMillis();
118    }
119
120    /**
121     * The ID of the repository, e.g., "central".
122     *
123     * @return The ID of the repository or an empty string if unknown, never {@code null}.
124     *
125     * @since 1.1.0
126     */
127    public String getRepositoryId() {
128        return repositoryId;
129    }
130
131    /**
132     * The base URL of the repository, e.g. "https://repo1.maven.org/maven2/". Unless the URL is unknown, it will be
133     * terminated by a trailing slash.
134     *
135     * @return The base URL of the repository or an empty string if unknown, never {@code null}.
136     */
137    public String getRepositoryUrl() {
138        return repositoryUrl;
139    }
140
141    /**
142     * The path of the resource relative to the repository's base URL, e.g. "org/apache/maven/maven/3.0/maven-3.0.pom".
143     *
144     * @return The path of the resource, never {@code null}.
145     */
146    public String getResourceName() {
147        return resourceName;
148    }
149
150    /**
151     * The representation of "resource", if any. The content of this field may be
152     * {@link org.eclipse.aether.artifact.Artifact} or {@link org.eclipse.aether.metadata.Metadata} or {@code null}
153     * in case of some legacy flow. Preferred way to handle returned value is with {@code instanceof}.
154     *
155     * @return The representation of this resource, may be {@code null}.
156     * @since 2.0.0
157     */
158    public Object getResource() {
159        return resource;
160    }
161
162    /**
163     * Gets the local file being uploaded or downloaded. When the repository system merely checks for the existence of a
164     * remote resource, no local file will be involved in the transfer.
165     *
166     * @return The source/target file involved in the transfer or {@code null} if none.
167     * @deprecated Use {@link #getPath()} instead.
168     */
169    @Deprecated
170    public File getFile() {
171        return path != null ? path.toFile() : null;
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     * @since 2.0.0
180     */
181    public Path getPath() {
182        return path;
183    }
184
185    /**
186     * The size of the resource in bytes. Note that the size of a resource during downloads might be unknown to the
187     * client which is usually the case when transfers employ compression like gzip. In general, the content length is
188     * not known until the transfer has {@link TransferListener#transferStarted(TransferEvent) started}.
189     *
190     * @return The size of the resource in bytes or a negative value if unknown.
191     */
192    public long getContentLength() {
193        return contentLength;
194    }
195
196    /**
197     * Sets the size of the resource in bytes.
198     *
199     * @param contentLength The size of the resource in bytes or a negative value if unknown.
200     * @return This resource for chaining, never {@code null}.
201     */
202    public TransferResource setContentLength(long contentLength) {
203        this.contentLength = contentLength;
204        return this;
205    }
206
207    /**
208     * Gets the byte offset within the resource from which the download starts. A positive offset indicates a previous
209     * download attempt is being resumed, {@code 0} means the transfer starts at the first byte.
210     *
211     * @return The zero-based index of the first byte being transferred, never negative.
212     */
213    public long getResumeOffset() {
214        return resumeOffset;
215    }
216
217    /**
218     * Sets the byte offset within the resource at which the download starts.
219     *
220     * @param resumeOffset The zero-based index of the first byte being transferred, must not be negative.
221     * @return This resource for chaining, never {@code null}.
222     */
223    public TransferResource setResumeOffset(long resumeOffset) {
224        if (resumeOffset < 0L) {
225            throw new IllegalArgumentException("resume offset cannot be negative");
226        }
227        this.resumeOffset = resumeOffset;
228        return this;
229    }
230
231    /**
232     * Gets the timestamp when the transfer of this resource was started.
233     *
234     * @return The timestamp when the transfer of this resource was started.
235     */
236    public long getTransferStartTime() {
237        return startTime;
238    }
239
240    /**
241     * Gets the trace information that describes the higher level request/operation during which this resource is
242     * transferred.
243     *
244     * @return The trace information about the higher level operation or {@code null} if none.
245     */
246    public RequestTrace getTrace() {
247        return trace;
248    }
249
250    @Override
251    public String toString() {
252        return getRepositoryUrl() + getResourceName() + " <> " + getPath();
253    }
254}