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           * @see TransferListener#transferInitiated(TransferEvent)
44           */
45          INITIATED,
46  
47          /**
48           * @see TransferListener#transferStarted(TransferEvent)
49           */
50          STARTED,
51  
52          /**
53           * @see TransferListener#transferProgressed(TransferEvent)
54           */
55          PROGRESSED,
56  
57          /**
58           * @see TransferListener#transferCorrupted(TransferEvent)
59           */
60          CORRUPTED,
61  
62          /**
63           * @see TransferListener#transferSucceeded(TransferEvent)
64           */
65          SUCCEEDED,
66  
67          /**
68           * @see TransferListener#transferFailed(TransferEvent)
69           */
70          FAILED
71      }
72  
73      /**
74       * The type of the request/transfer being performed.
75       */
76      public enum RequestType {
77  
78          /**
79           * Download artifact/metadata.
80           */
81          GET,
82  
83          /**
84           * Check artifact/metadata existence only.
85           */
86          GET_EXISTENCE,
87  
88          /**
89           * Upload artifact/metadata.
90           */
91          PUT,
92      }
93  
94      private final EventType type;
95  
96      private final RequestType requestType;
97  
98      private final RepositorySystemSession session;
99  
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 }