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.nio.ByteBuffer; 22 23 import org.eclipse.aether.RepositorySystemSession; 24 25 import static java.util.Objects.requireNonNull; 26 27 /** 28 * An event fired to a transfer listener during an artifact/metadata transfer. 29 * 30 * @see TransferListener 31 * @see TransferEvent.Builder 32 */ 33 public final class TransferEvent { 34 35 /** 36 * The type of the event. 37 */ 38 public enum EventType { 39 40 /** 41 * @see TransferListener#transferInitiated(TransferEvent) 42 */ 43 INITIATED, 44 45 /** 46 * @see TransferListener#transferStarted(TransferEvent) 47 */ 48 STARTED, 49 50 /** 51 * @see TransferListener#transferProgressed(TransferEvent) 52 */ 53 PROGRESSED, 54 55 /** 56 * @see TransferListener#transferCorrupted(TransferEvent) 57 */ 58 CORRUPTED, 59 60 /** 61 * @see TransferListener#transferSucceeded(TransferEvent) 62 */ 63 SUCCEEDED, 64 65 /** 66 * @see TransferListener#transferFailed(TransferEvent) 67 */ 68 FAILED 69 } 70 71 /** 72 * The type of the request/transfer being performed. 73 */ 74 public enum RequestType { 75 76 /** 77 * Download artifact/metadata. 78 */ 79 GET, 80 81 /** 82 * Check artifact/metadata existence only. 83 */ 84 GET_EXISTENCE, 85 86 /** 87 * Upload artifact/metadata. 88 */ 89 PUT, 90 } 91 92 private final EventType type; 93 94 private final RequestType requestType; 95 96 private final RepositorySystemSession session; 97 98 private final TransferResource resource; 99 100 private final ByteBuffer dataBuffer; 101 102 private final long transferredBytes; 103 104 private final Exception exception; 105 106 TransferEvent(Builder builder) { 107 type = builder.type; 108 requestType = builder.requestType; 109 session = builder.session; 110 resource = builder.resource; 111 dataBuffer = builder.dataBuffer; 112 transferredBytes = builder.transferredBytes; 113 exception = builder.exception; 114 } 115 116 /** 117 * Gets the type of the event. 118 * 119 * @return The type of the event, never {@code null}. 120 */ 121 public EventType getType() { 122 return type; 123 } 124 125 /** 126 * Gets the type of the request/transfer. 127 * 128 * @return The type of the request/transfer, never {@code null}. 129 */ 130 public RequestType getRequestType() { 131 return requestType; 132 } 133 134 /** 135 * Gets the repository system session during which the event occurred. 136 * 137 * @return The repository system session during which the event occurred, never {@code null}. 138 */ 139 public RepositorySystemSession getSession() { 140 return session; 141 } 142 143 /** 144 * Gets the resource that is being transferred. 145 * 146 * @return The resource being transferred, never {@code null}. 147 */ 148 public TransferResource getResource() { 149 return resource; 150 } 151 152 /** 153 * Gets the total number of bytes that have been transferred since the download/upload of the resource was started. 154 * If a download has been resumed, the returned count includes the bytes that were already downloaded during the 155 * previous attempt. In other words, the ratio of transferred bytes to the content length of the resource indicates 156 * the percentage of transfer completion. 157 * 158 * @return The total number of bytes that have been transferred since the transfer started, never negative. 159 * @see #getDataLength() 160 * @see TransferResource#getResumeOffset() 161 */ 162 public long getTransferredBytes() { 163 return transferredBytes; 164 } 165 166 /** 167 * Gets the byte buffer holding the transferred bytes since the last event. A listener must assume this buffer to be 168 * owned by the event source and must not change any byte in this buffer. Also, the buffer is only valid for the 169 * duration of the event callback, i.e. the next event might reuse the same buffer (with updated contents). 170 * Therefore, if the actual event processing is deferred, the byte buffer would have to be cloned to create an 171 * immutable snapshot of its contents. 172 * 173 * @return The (read-only) byte buffer or {@code null} if not applicable to the event, i.e. if the event type is not 174 * {@link EventType#PROGRESSED}. 175 */ 176 public ByteBuffer getDataBuffer() { 177 return (dataBuffer != null) ? dataBuffer.asReadOnlyBuffer() : null; 178 } 179 180 /** 181 * Gets the number of bytes that have been transferred since the last event. 182 * 183 * @return The number of bytes that have been transferred since the last event, possibly zero but never negative. 184 * @see #getTransferredBytes() 185 */ 186 public int getDataLength() { 187 return (dataBuffer != null) ? dataBuffer.remaining() : 0; 188 } 189 190 /** 191 * Gets the error that occurred during the transfer. 192 * 193 * @return The error that occurred or {@code null} if none. 194 */ 195 public Exception getException() { 196 return exception; 197 } 198 199 @Override 200 public String toString() { 201 return getRequestType() + " " + getType() + " " + getResource(); 202 } 203 204 /** 205 * A builder to create transfer events. 206 */ 207 public static final class Builder { 208 209 EventType type; 210 211 RequestType requestType; 212 213 final RepositorySystemSession session; 214 215 final TransferResource resource; 216 217 ByteBuffer dataBuffer; 218 219 long transferredBytes; 220 221 Exception exception; 222 223 /** 224 * Creates a new transfer event builder for the specified session and the given resource. 225 * 226 * @param session The repository system session, must not be {@code null}. 227 * @param resource The resource being transferred, must not be {@code null}. 228 */ 229 public Builder(RepositorySystemSession session, TransferResource resource) { 230 this.session = requireNonNull(session, "repository system session cannot be null"); 231 this.resource = requireNonNull(resource, "transfer resource cannot be null"); 232 type = EventType.INITIATED; 233 requestType = RequestType.GET; 234 } 235 236 private Builder(Builder prototype) { 237 session = prototype.session; 238 resource = prototype.resource; 239 type = prototype.type; 240 requestType = prototype.requestType; 241 dataBuffer = prototype.dataBuffer; 242 transferredBytes = prototype.transferredBytes; 243 exception = prototype.exception; 244 } 245 246 /** 247 * Creates a new transfer event builder from the current values of this builder. The state of this builder 248 * remains unchanged. 249 * 250 * @return The new event builder, never {@code null}. 251 */ 252 public Builder copy() { 253 return new Builder(this); 254 } 255 256 /** 257 * Sets the type of the event and resets event-specific fields. In more detail, the data buffer and the 258 * exception fields are set to {@code null}. Furthermore, the total number of transferred bytes is set to 259 * {@code 0} if the event type is {@link EventType#STARTED}. 260 * 261 * @param type The type of the event, must not be {@code null}. 262 * @return This event builder for chaining, never {@code null}. 263 */ 264 public Builder resetType(EventType type) { 265 this.type = requireNonNull(type, "event type cannot be null"); 266 dataBuffer = null; 267 exception = null; 268 switch (type) { 269 case INITIATED: 270 case STARTED: 271 transferredBytes = 0L; 272 default: 273 } 274 return this; 275 } 276 277 /** 278 * Sets the type of the event. When re-using the same builder to generate a sequence of events for one transfer, 279 * {@link #resetType(TransferEvent.EventType)} might be more handy. 280 * 281 * @param type The type of the event, must not be {@code null}. 282 * @return This event builder for chaining, never {@code null}. 283 */ 284 public Builder setType(EventType type) { 285 this.type = requireNonNull(type, "event type cannot be null"); 286 return this; 287 } 288 289 /** 290 * Sets the type of the request/transfer. 291 * 292 * @param requestType The request/transfer type, must not be {@code null}. 293 * @return This event builder for chaining, never {@code null}. 294 */ 295 public Builder setRequestType(RequestType requestType) { 296 this.requestType = requireNonNull(requestType, "request type cannot be null"); 297 return this; 298 } 299 300 /** 301 * Sets the total number of bytes that have been transferred so far during the download/upload of the resource. 302 * If a download is being resumed, the count must include the bytes that were already downloaded in the previous 303 * attempt and from which the current transfer started. In this case, the event type {@link EventType#STARTED} 304 * should indicate from what byte the download resumes. 305 * 306 * @param transferredBytes The total number of bytes that have been transferred so far during the 307 * download/upload of the resource, must not be negative. 308 * @return This event builder for chaining, never {@code null}. 309 * @see TransferResource#setResumeOffset(long) 310 */ 311 public Builder setTransferredBytes(long transferredBytes) { 312 if (transferredBytes < 0L) { 313 throw new IllegalArgumentException("number of transferred bytes cannot be negative"); 314 } 315 this.transferredBytes = transferredBytes; 316 return this; 317 } 318 319 /** 320 * Increments the total number of bytes that have been transferred so far during the download/upload. 321 * 322 * @param transferredBytes The number of bytes that have been transferred since the last event, must not be 323 * negative. 324 * @return This event builder for chaining, never {@code null}. 325 */ 326 public Builder addTransferredBytes(long transferredBytes) { 327 if (transferredBytes < 0L) { 328 throw new IllegalArgumentException("number of transferred bytes cannot be negative"); 329 } 330 this.transferredBytes += transferredBytes; 331 return this; 332 } 333 334 /** 335 * Sets the byte buffer holding the transferred bytes since the last event. 336 * 337 * @param buffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if not 338 * applicable to the event. 339 * @param offset The starting point of valid bytes in the array. 340 * @param length The number of valid bytes, must not be negative. 341 * @return This event builder for chaining, never {@code null}. 342 */ 343 public Builder setDataBuffer(byte[] buffer, int offset, int length) { 344 return setDataBuffer((buffer != null) ? ByteBuffer.wrap(buffer, offset, length) : null); 345 } 346 347 /** 348 * Sets the byte buffer holding the transferred bytes since the last event. 349 * 350 * @param dataBuffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if 351 * not applicable to the event. 352 * @return This event builder for chaining, never {@code null}. 353 */ 354 public Builder setDataBuffer(ByteBuffer dataBuffer) { 355 this.dataBuffer = dataBuffer; 356 return this; 357 } 358 359 /** 360 * Sets the error that occurred during the transfer. 361 * 362 * @param exception The error that occurred during the transfer, may be {@code null} if none. 363 * @return This event builder for chaining, never {@code null}. 364 */ 365 public Builder setException(Exception exception) { 366 this.exception = exception; 367 return this; 368 } 369 370 /** 371 * Builds a new transfer event from the current values of this builder. The state of the builder itself remains 372 * unchanged. 373 * 374 * @return The transfer event, never {@code null}. 375 */ 376 public TransferEvent build() { 377 return new TransferEvent(this); 378 } 379 } 380 }