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.nio.ByteBuffer; 022import java.util.Collections; 023import java.util.Map; 024 025import org.eclipse.aether.RepositorySystemSession; 026 027import static java.util.Objects.requireNonNull; 028 029/** 030 * An event fired to a transfer listener during an artifact/metadata transfer. 031 * 032 * @see TransferListener 033 * @see TransferEvent.Builder 034 */ 035public final class TransferEvent { 036 037 /** 038 * The type of the event. 039 */ 040 public enum EventType { 041 042 /** 043 * The transfer has been initiated. 044 * 045 * @see TransferListener#transferInitiated(TransferEvent) 046 */ 047 INITIATED, 048 049 /** 050 * The transfer has started. 051 * 052 * @see TransferListener#transferStarted(TransferEvent) 053 */ 054 STARTED, 055 056 /** 057 * The transfer is progressing. 058 * 059 * @see TransferListener#transferProgressed(TransferEvent) 060 */ 061 PROGRESSED, 062 063 /** 064 * The transferred data is corrupt. 065 * 066 * @see TransferListener#transferCorrupted(TransferEvent) 067 */ 068 CORRUPTED, 069 070 /** 071 * The transfer has succeeded. 072 * 073 * @see TransferListener#transferSucceeded(TransferEvent) 074 */ 075 SUCCEEDED, 076 077 /** 078 * The transfer has failed. 079 * 080 * @see TransferListener#transferFailed(TransferEvent) 081 */ 082 FAILED 083 } 084 085 /** 086 * The type of the request/transfer being performed. 087 */ 088 public enum RequestType { 089 090 /** 091 * Download artifact/metadata. 092 */ 093 GET, 094 095 /** 096 * Check artifact/metadata existence only. 097 */ 098 GET_EXISTENCE, 099 100 /** 101 * Upload artifact/metadata. 102 */ 103 PUT, 104 } 105 106 private final EventType type; 107 108 private final RequestType requestType; 109 110 private final RepositorySystemSession session; 111 112 private final TransferResource resource; 113 114 private final ByteBuffer dataBuffer; 115 116 private final long transferredBytes; 117 118 private final Exception exception; 119 120 private final Map<TransportPropertyKey, Object> transportProperties; 121 122 TransferEvent(Builder builder) { 123 type = builder.type; 124 requestType = builder.requestType; 125 session = builder.session; 126 resource = builder.resource; 127 dataBuffer = builder.dataBuffer; 128 transferredBytes = builder.transferredBytes; 129 exception = builder.exception; 130 transportProperties = builder.transportProperties; 131 } 132 133 /** 134 * Gets the type of the event. 135 * 136 * @return The type of the event, never {@code null}. 137 */ 138 public EventType getType() { 139 return type; 140 } 141 142 /** 143 * Gets the type of the request/transfer. 144 * 145 * @return The type of the request/transfer, never {@code null}. 146 */ 147 public RequestType getRequestType() { 148 return requestType; 149 } 150 151 /** 152 * Gets the repository system session during which the event occurred. 153 * 154 * @return The repository system session during which the event occurred, never {@code null}. 155 */ 156 public RepositorySystemSession getSession() { 157 return session; 158 } 159 160 /** 161 * Gets the resource that is being transferred. 162 * 163 * @return The resource being transferred, never {@code null}. 164 */ 165 public TransferResource getResource() { 166 return resource; 167 } 168 169 /** 170 * Gets the total number of bytes that have been transferred since the download/upload of the resource was started. 171 * If a download has been resumed, the returned count includes the bytes that were already downloaded during the 172 * previous attempt. In other words, the ratio of transferred bytes to the content length of the resource indicates 173 * the percentage of transfer completion. 174 * 175 * @return The total number of bytes that have been transferred since the transfer started, never negative. 176 * @see #getDataLength() 177 * @see TransferResource#getResumeOffset() 178 */ 179 public long getTransferredBytes() { 180 return transferredBytes; 181 } 182 183 /** 184 * Gets the byte buffer holding the transferred bytes since the last event. A listener must assume this buffer to be 185 * owned by the event source and must not change any byte in this buffer. Also, the buffer is only valid for the 186 * duration of the event callback, i.e. the next event might reuse the same buffer (with updated contents). 187 * Therefore, if the actual event processing is deferred, the byte buffer would have to be cloned to create an 188 * immutable snapshot of its contents. 189 * 190 * @return The (read-only) byte buffer or {@code null} if not applicable to the event, i.e. if the event type is not 191 * {@link EventType#PROGRESSED}. 192 */ 193 public ByteBuffer getDataBuffer() { 194 return (dataBuffer != null) ? dataBuffer.asReadOnlyBuffer() : null; 195 } 196 197 /** 198 * Gets the number of bytes that have been transferred since the last event. 199 * 200 * @return The number of bytes that have been transferred since the last event, possibly zero but never negative. 201 * @see #getTransferredBytes() 202 */ 203 public int getDataLength() { 204 return (dataBuffer != null) ? dataBuffer.remaining() : 0; 205 } 206 207 /** 208 * Gets the error that occurred during the transfer. 209 * 210 * @return The error that occurred or {@code null} if none. 211 */ 212 public Exception getException() { 213 return exception; 214 } 215 216 /** 217 * Get the transport properties associated with this transfer. 218 * The keys are transporter specific and the value types are key specific. 219 * This is only potentially not empty for the following events: 220 * <ul> 221 * <li>{@link EventType#CORRUPTED}</li> 222 * <li>{@link EventType#FAILED}</li> 223 * <li>{@link EventType#SUCCEEDED}</li> 224 * </ul> 225 * @return The immutable transport properties associated with this transfer, may be empty. 226 * @since 2.0.21 227 * @see HttpTransportProperty.Key HttpTransportProperty.Key for HTTP specific keys 228 */ 229 public Map<TransportPropertyKey, Object> getTransportProperties() { 230 return transportProperties; 231 } 232 233 @Override 234 public String toString() { 235 return getRequestType() + " " + getType() + " " + getResource(); 236 } 237 238 /** 239 * A builder to create transfer events. 240 */ 241 public static final class Builder { 242 243 EventType type; 244 245 RequestType requestType; 246 247 final RepositorySystemSession session; 248 249 final TransferResource resource; 250 251 ByteBuffer dataBuffer; 252 253 long transferredBytes; 254 255 Exception exception; 256 257 Map<TransportPropertyKey, Object> transportProperties; 258 259 /** 260 * Creates a new transfer event builder for the specified session and the given resource. 261 * 262 * @param session The repository system session, must not be {@code null}. 263 * @param resource The resource being transferred, must not be {@code null}. 264 */ 265 public Builder(RepositorySystemSession session, TransferResource resource) { 266 this.session = requireNonNull(session, "repository system session cannot be null"); 267 this.resource = requireNonNull(resource, "transfer resource cannot be null"); 268 type = EventType.INITIATED; 269 requestType = RequestType.GET; 270 transportProperties = Collections.emptyMap(); 271 } 272 273 private Builder(Builder prototype) { 274 session = prototype.session; 275 resource = prototype.resource; 276 type = prototype.type; 277 requestType = prototype.requestType; 278 dataBuffer = prototype.dataBuffer; 279 transferredBytes = prototype.transferredBytes; 280 exception = prototype.exception; 281 transportProperties = prototype.transportProperties; 282 } 283 284 /** 285 * Creates a new transfer event builder from the current values of this builder. The state of this builder 286 * remains unchanged. 287 * 288 * @return The new event builder, never {@code null}. 289 */ 290 public Builder copy() { 291 return new Builder(this); 292 } 293 294 /** 295 * Sets the type of the event and resets event-specific fields. In more detail, the data buffer and the 296 * exception fields are set to {@code null}. Furthermore, the total number of transferred bytes is set to 297 * {@code 0} if the event type is {@link EventType#STARTED}. 298 * 299 * @param type The type of the event, must not be {@code null}. 300 * @return This event builder for chaining, never {@code null}. 301 */ 302 public Builder resetType(EventType type) { 303 this.type = requireNonNull(type, "event type cannot be null"); 304 dataBuffer = null; 305 exception = null; 306 switch (type) { 307 case INITIATED: 308 case STARTED: 309 transferredBytes = 0L; 310 default: 311 } 312 return this; 313 } 314 315 /** 316 * Sets the type of the event. When re-using the same builder to generate a sequence of events for one transfer, 317 * {@link #resetType(TransferEvent.EventType)} might be more handy. 318 * 319 * @param type The type of the event, must not be {@code null}. 320 * @return This event builder for chaining, never {@code null}. 321 */ 322 public Builder setType(EventType type) { 323 this.type = requireNonNull(type, "event type cannot be null"); 324 return this; 325 } 326 327 /** 328 * Sets the type of the request/transfer. 329 * 330 * @param requestType The request/transfer type, must not be {@code null}. 331 * @return This event builder for chaining, never {@code null}. 332 */ 333 public Builder setRequestType(RequestType requestType) { 334 this.requestType = requireNonNull(requestType, "request type cannot be null"); 335 return this; 336 } 337 338 /** 339 * Sets the total number of bytes that have been transferred so far during the download/upload of the resource. 340 * If a download is being resumed, the count must include the bytes that were already downloaded in the previous 341 * attempt and from which the current transfer started. In this case, the event type {@link EventType#STARTED} 342 * should indicate from what byte the download resumes. 343 * 344 * @param transferredBytes The total number of bytes that have been transferred so far during the 345 * download/upload of the resource, must not be negative. 346 * @return This event builder for chaining, never {@code null}. 347 * @see TransferResource#setResumeOffset(long) 348 */ 349 public Builder setTransferredBytes(long transferredBytes) { 350 if (transferredBytes < 0L) { 351 throw new IllegalArgumentException("number of transferred bytes cannot be negative"); 352 } 353 this.transferredBytes = transferredBytes; 354 return this; 355 } 356 357 /** 358 * Increments the total number of bytes that have been transferred so far during the download/upload. 359 * 360 * @param transferredBytes The number of bytes that have been transferred since the last event, must not be 361 * negative. 362 * @return This event builder for chaining, never {@code null}. 363 */ 364 public Builder addTransferredBytes(long transferredBytes) { 365 if (transferredBytes < 0L) { 366 throw new IllegalArgumentException("number of transferred bytes cannot be negative"); 367 } 368 this.transferredBytes += transferredBytes; 369 return this; 370 } 371 372 /** 373 * Sets the byte buffer holding the transferred bytes since the last event. 374 * 375 * @param buffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if not 376 * applicable to the event. 377 * @param offset The starting point of valid bytes in the array. 378 * @param length The number of valid bytes, must not be negative. 379 * @return This event builder for chaining, never {@code null}. 380 */ 381 public Builder setDataBuffer(byte[] buffer, int offset, int length) { 382 return setDataBuffer((buffer != null) ? ByteBuffer.wrap(buffer, offset, length) : null); 383 } 384 385 /** 386 * Sets the byte buffer holding the transferred bytes since the last event. 387 * 388 * @param dataBuffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if 389 * not applicable to the event. 390 * @return This event builder for chaining, never {@code null}. 391 */ 392 public Builder setDataBuffer(ByteBuffer dataBuffer) { 393 this.dataBuffer = dataBuffer; 394 return this; 395 } 396 397 /** 398 * Sets the error that occurred during the transfer. 399 * 400 * @param exception The error that occurred during the transfer, may be {@code null} if none. 401 * @return This event builder for chaining, never {@code null}. 402 */ 403 public Builder setException(Exception exception) { 404 this.exception = exception; 405 return this; 406 } 407 408 /** 409 * Sets the transport properties associated with this transfer. The keys are transporter specific and the value types are key specific. 410 * @param transportProperties The transport properties used in the underlying transfer, must not be {@code null}. 411 * @return This event builder for chaining, never {@code null}. 412 */ 413 public Builder setTransportProperties(Map<TransportPropertyKey, Object> transportProperties) { 414 requireNonNull(transportProperties, "transportProperties cannot be null"); 415 this.transportProperties = Collections.unmodifiableMap(transportProperties); 416 return this; 417 } 418 419 /** 420 * Builds a new transfer event from the current values of this builder. The state of the builder itself remains 421 * unchanged. 422 * 423 * @return The transfer event, never {@code null}. 424 */ 425 public TransferEvent build() { 426 return new TransferEvent(this); 427 } 428 } 429 430 /** 431 * Marker interface for keys used in {@link #getTransportProperties()}. 432 * 433 * @since 2.0.21 434 */ 435 public interface TransportPropertyKey {} 436}