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;
022
023import org.eclipse.aether.RepositorySystemSession;
024
025import static java.util.Objects.requireNonNull;
026
027/**
028 * An event fired to a transfer listener during an artifact/metadata transfer.
029 *
030 * @see TransferListener
031 * @see TransferEvent.Builder
032 */
033public final class TransferEvent {
034
035    /**
036     * The type of the event.
037     */
038    public enum EventType {
039
040        /**
041         * @see TransferListener#transferInitiated(TransferEvent)
042         */
043        INITIATED,
044
045        /**
046         * @see TransferListener#transferStarted(TransferEvent)
047         */
048        STARTED,
049
050        /**
051         * @see TransferListener#transferProgressed(TransferEvent)
052         */
053        PROGRESSED,
054
055        /**
056         * @see TransferListener#transferCorrupted(TransferEvent)
057         */
058        CORRUPTED,
059
060        /**
061         * @see TransferListener#transferSucceeded(TransferEvent)
062         */
063        SUCCEEDED,
064
065        /**
066         * @see TransferListener#transferFailed(TransferEvent)
067         */
068        FAILED
069    }
070
071    /**
072     * The type of the request/transfer being performed.
073     */
074    public enum RequestType {
075
076        /**
077         * Download artifact/metadata.
078         */
079        GET,
080
081        /**
082         * Check artifact/metadata existence only.
083         */
084        GET_EXISTENCE,
085
086        /**
087         * Upload artifact/metadata.
088         */
089        PUT,
090    }
091
092    private final EventType type;
093
094    private final RequestType requestType;
095
096    private final RepositorySystemSession session;
097
098    private final TransferResource resource;
099
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}