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 }