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