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 }