View Javadoc
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  import java.util.Collections;
23  import java.util.Map;
24  
25  import org.eclipse.aether.RepositorySystemSession;
26  
27  import static java.util.Objects.requireNonNull;
28  
29  /**
30   * An event fired to a transfer listener during an artifact/metadata transfer.
31   *
32   * @see TransferListener
33   * @see TransferEvent.Builder
34   */
35  public final class TransferEvent {
36  
37      /**
38       * The type of the event.
39       */
40      public enum EventType {
41  
42          /**
43           * The transfer has been initiated.
44           *
45           * @see TransferListener#transferInitiated(TransferEvent)
46           */
47          INITIATED,
48  
49          /**
50           * The transfer has started.
51           *
52           * @see TransferListener#transferStarted(TransferEvent)
53           */
54          STARTED,
55  
56          /**
57           * The transfer is progressing.
58           *
59           * @see TransferListener#transferProgressed(TransferEvent)
60           */
61          PROGRESSED,
62  
63          /**
64           * The transferred data is corrupt.
65           *
66           * @see TransferListener#transferCorrupted(TransferEvent)
67           */
68          CORRUPTED,
69  
70          /**
71           * The transfer has succeeded.
72           *
73           * @see TransferListener#transferSucceeded(TransferEvent)
74           */
75          SUCCEEDED,
76  
77          /**
78           * The transfer has failed.
79           *
80           * @see TransferListener#transferFailed(TransferEvent)
81           */
82          FAILED
83      }
84  
85      /**
86       * The type of the request/transfer being performed.
87       */
88      public enum RequestType {
89  
90          /**
91           * Download artifact/metadata.
92           */
93          GET,
94  
95          /**
96           * Check artifact/metadata existence only.
97           */
98          GET_EXISTENCE,
99  
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 }