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