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