1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.eclipse.aether.transfer;
20
21 import java.io.File;
22 import java.nio.file.Path;
23 import java.time.Clock;
24 import java.time.Instant;
25
26 import org.eclipse.aether.RequestTrace;
27
28 /**
29 * Describes a resource being uploaded or downloaded by the repository system.
30 */
31 public final class TransferResource {
32
33 private static Clock clock = Clock.systemUTC();
34
35 private final String repositoryId;
36
37 private final String repositoryUrl;
38
39 private final String resourceName;
40
41 private final Object resource;
42
43 private final Path path;
44
45 private final Instant startTime;
46
47 private final RequestTrace trace;
48
49 private long contentLength = -1L;
50
51 private long resumeOffset;
52
53 public static Clock getClock() {
54 return clock;
55 }
56
57 public static void setClock(Clock clock) {
58 TransferResource.clock = clock;
59 }
60
61 /**
62 * Creates a new transfer resource with the specified properties.
63 *
64 * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
65 * empty if unknown.
66 * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
67 * trailing slash will automatically be added if missing.
68 * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
69 * (if any) will be automatically removed.
70 * @param file The source/target file involved in the transfer, may be {@code null}.
71 * @param trace The trace information, may be {@code null}.
72 *
73 * @since 1.1.0
74 * @deprecated Use {@link TransferResource(String, String, String, Path, Object, RequestTrace)} instead.
75 */
76 @Deprecated
77 public TransferResource(
78 String repositoryId, String repositoryUrl, String resourceName, File file, RequestTrace trace) {
79 this(repositoryId, repositoryUrl, resourceName, file != null ? file.toPath() : null, null, trace);
80 }
81
82 /**
83 * Creates a new transfer resource with the specified properties.
84 *
85 * @param repositoryId The ID of the repository used to transfer the resource, may be {@code null} or
86 * empty if unknown.
87 * @param repositoryUrl The base URL of the repository, may be {@code null} or empty if unknown. If not empty, a
88 * trailing slash will automatically be added if missing.
89 * @param resourceName The relative path to the resource within the repository, may be {@code null}. A leading slash
90 * (if any) will be automatically removed.
91 * @param path The source/target file involved in the transfer, may be {@code null}.
92 * @param resource The representation of this resource, may be {@code null}.
93 * @param trace The trace information, may be {@code null}.
94 *
95 * @since 2.0.0
96 */
97 public TransferResource(
98 String repositoryId,
99 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 }