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