--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010, 2013 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.aether.transfer;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * An event fired to a transfer listener during an artifact/metadata transfer.
+ *
+ * @see TransferListener
+ * @see TransferEvent.Builder
+ */
+public final class TransferEvent
+{
+
+ /**
+ * The type of the event.
+ */
+ public enum EventType
+ {
+
+ /**
+ * @see TransferListener#transferInitiated(TransferEvent)
+ */
+ INITIATED,
+
+ /**
+ * @see TransferListener#transferStarted(TransferEvent)
+ */
+ STARTED,
+
+ /**
+ * @see TransferListener#transferProgressed(TransferEvent)
+ */
+ PROGRESSED,
+
+ /**
+ * @see TransferListener#transferCorrupted(TransferEvent)
+ */
+ CORRUPTED,
+
+ /**
+ * @see TransferListener#transferSucceeded(TransferEvent)
+ */
+ SUCCEEDED,
+
+ /**
+ * @see TransferListener#transferFailed(TransferEvent)
+ */
+ FAILED
+
+ }
+
+ /**
+ * The type of the request/transfer being performed.
+ */
+ public enum RequestType
+ {
+
+ /**
+ * Download artifact/metadata.
+ */
+ GET,
+
+ /**
+ * Check artifact/metadata existence only.
+ */
+ GET_EXISTENCE,
+
+ /**
+ * Upload artifact/metadata.
+ */
+ PUT,
+
+ }
+
+ private final EventType type;
+
+ private final RequestType requestType;
+
+ private final RepositorySystemSession session;
+
+ private final TransferResource resource;
+
+ private final ByteBuffer dataBuffer;
+
+ private final long transferredBytes;
+
+ private final Exception exception;
+
+ TransferEvent( Builder builder )
+ {
+ type = builder.type;
+ requestType = builder.requestType;
+ session = builder.session;
+ resource = builder.resource;
+ dataBuffer = builder.dataBuffer;
+ transferredBytes = builder.transferredBytes;
+ exception = builder.exception;
+ }
+
+ /**
+ * Gets the type of the event.
+ *
+ * @return The type of the event, never {@code null}.
+ */
+ public EventType getType()
+ {
+ return type;
+ }
+
+ /**
+ * Gets the type of the request/transfer.
+ *
+ * @return The type of the request/transfer, never {@code null}.
+ */
+ public RequestType getRequestType()
+ {
+ return requestType;
+ }
+
+ /**
+ * Gets the repository system session during which the event occurred.
+ *
+ * @return The repository system session during which the event occurred, never {@code null}.
+ */
+ public RepositorySystemSession getSession()
+ {
+ return session;
+ }
+
+ /**
+ * Gets the resource that is being transferred.
+ *
+ * @return The resource being transferred, never {@code null}.
+ */
+ public TransferResource getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Gets the total number of bytes that have been transferred since the download/upload of the resource was started.
+ * If a download has been resumed, the returned count includes the bytes that were already downloaded during the
+ * previous attempt. In other words, the ratio of transferred bytes to the content length of the resource indicates
+ * the percentage of transfer completion.
+ *
+ * @return The total number of bytes that have been transferred since the transfer started, never negative.
+ * @see #getDataLength()
+ * @see TransferResource#getResumeOffset()
+ */
+ public long getTransferredBytes()
+ {
+ return transferredBytes;
+ }
+
+ /**
+ * Gets the byte buffer holding the transferred bytes since the last event. A listener must assume this buffer to be
+ * owned by the event source and must not change any byte in this buffer. Also, the buffer is only valid for the
+ * duration of the event callback, i.e. the next event might reuse the same buffer (with updated contents).
+ * Therefore, if the actual event processing is deferred, the byte buffer would have to be cloned to create an
+ * immutable snapshot of its contents.
+ *
+ * @return The (read-only) byte buffer or {@code null} if not applicable to the event, i.e. if the event type is not
+ * {@link EventType#PROGRESSED}.
+ */
+ public ByteBuffer getDataBuffer()
+ {
+ return ( dataBuffer != null ) ? dataBuffer.asReadOnlyBuffer() : null;
+ }
+
+ /**
+ * Gets the number of bytes that have been transferred since the last event.
+ *
+ * @return The number of bytes that have been transferred since the last event, possibly zero but never negative.
+ * @see #getTransferredBytes()
+ */
+ public int getDataLength()
+ {
+ return ( dataBuffer != null ) ? dataBuffer.remaining() : 0;
+ }
+
+ /**
+ * Gets the error that occurred during the transfer.
+ *
+ * @return The error that occurred or {@code null} if none.
+ */
+ public Exception getException()
+ {
+ return exception;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getRequestType() + " " + getType() + " " + getResource();
+ }
+
+ /**
+ * A builder to create transfer events.
+ */
+ public static final class Builder
+ {
+
+ EventType type;
+
+ RequestType requestType;
+
+ RepositorySystemSession session;
+
+ TransferResource resource;
+
+ ByteBuffer dataBuffer;
+
+ long transferredBytes;
+
+ Exception exception;
+
+ /**
+ * Creates a new transfer event builder for the specified session and the given resource.
+ *
+ * @param session The repository system session, must not be {@code null}.
+ * @param resource The resource being transferred, must not be {@code null}.
+ */
+ public Builder( RepositorySystemSession session, TransferResource resource )
+ {
+ if ( session == null )
+ {
+ throw new IllegalArgumentException( "session not specified" );
+ }
+ if ( resource == null )
+ {
+ throw new IllegalArgumentException( "transfer resource not specified" );
+ }
+ this.session = session;
+ this.resource = resource;
+ type = EventType.INITIATED;
+ requestType = RequestType.GET;
+ }
+
+ private Builder( Builder prototype )
+ {
+ session = prototype.session;
+ resource = prototype.resource;
+ type = prototype.type;
+ requestType = prototype.requestType;
+ dataBuffer = prototype.dataBuffer;
+ transferredBytes = prototype.transferredBytes;
+ exception = prototype.exception;
+ }
+
+ /**
+ * Creates a new transfer event builder from the current values of this builder. The state of this builder
+ * remains unchanged.
+ *
+ * @return The new event builder, never {@code null}.
+ */
+ public Builder copy()
+ {
+ return new Builder( this );
+ }
+
+ /**
+ * Sets the type of the event and resets event-specific fields. In more detail, the data buffer and the
+ * exception fields are set to {@code null}. Furthermore, the total number of transferred bytes is set to
+ * {@code 0} if the event type is {@link EventType#STARTED}.
+ *
+ * @param type The type of the event, must not be {@code null}.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder resetType( EventType type )
+ {
+ if ( type == null )
+ {
+ throw new IllegalArgumentException( "event type not specified" );
+ }
+ this.type = type;
+ dataBuffer = null;
+ exception = null;
+ switch ( type )
+ {
+ case INITIATED:
+ case STARTED:
+ transferredBytes = 0;
+ default:
+ }
+ return this;
+ }
+
+ /**
+ * Sets the type of the event. When re-using the same builder to generate a sequence of events for one transfer,
+ * {@link #resetType(TransferEvent.EventType)} might be more handy.
+ *
+ * @param type The type of the event, must not be {@code null}.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setType( EventType type )
+ {
+ if ( type == null )
+ {
+ throw new IllegalArgumentException( "event type not specified" );
+ }
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Sets the type of the request/transfer.
+ *
+ * @param requestType The request/transfer type, must not be {@code null}.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setRequestType( RequestType requestType )
+ {
+ if ( requestType == null )
+ {
+ throw new IllegalArgumentException( "request type not specified" );
+ }
+ this.requestType = requestType;
+ return this;
+ }
+
+ /**
+ * Sets the total number of bytes that have been transferred so far during the download/upload of the resource.
+ * If a download is being resumed, the count must include the bytes that were already downloaded in the previous
+ * attempt and from which the current transfer started. In this case, the event type {@link EventType#STARTED}
+ * should indicate from what byte the download resumes.
+ *
+ * @param transferredBytes The total number of bytes that have been transferred so far during the
+ * download/upload of the resource, must not be negative.
+ * @return This event builder for chaining, never {@code null}.
+ * @see TransferResource#setResumeOffset(long)
+ */
+ public Builder setTransferredBytes( long transferredBytes )
+ {
+ if ( transferredBytes < 0 )
+ {
+ throw new IllegalArgumentException( "number of transferred bytes cannot be negative" );
+ }
+ this.transferredBytes = transferredBytes;
+ return this;
+ }
+
+ /**
+ * Increments the total number of bytes that have been transferred so far during the download/upload.
+ *
+ * @param transferredBytes The number of bytes that have been transferred since the last event, must not be
+ * negative.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder addTransferredBytes( long transferredBytes )
+ {
+ if ( transferredBytes < 0 )
+ {
+ throw new IllegalArgumentException( "number of transferred bytes cannot be negative" );
+ }
+ this.transferredBytes += transferredBytes;
+ return this;
+ }
+
+ /**
+ * Sets the byte buffer holding the transferred bytes since the last event.
+ *
+ * @param buffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if not
+ * applicable to the event.
+ * @param offset The starting point of valid bytes in the array.
+ * @param length The number of valid bytes, must not be negative.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setDataBuffer( byte[] buffer, int offset, int length )
+ {
+ return setDataBuffer( ( buffer != null ) ? ByteBuffer.wrap( buffer, offset, length ) : null );
+ }
+
+ /**
+ * Sets the byte buffer holding the transferred bytes since the last event.
+ *
+ * @param dataBuffer The byte buffer holding the transferred bytes since the last event, may be {@code null} if
+ * not applicable to the event.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setDataBuffer( ByteBuffer dataBuffer )
+ {
+ this.dataBuffer = dataBuffer;
+ return this;
+ }
+
+ /**
+ * Sets the error that occurred during the transfer.
+ *
+ * @param exception The error that occurred during the transfer, may be {@code null} if none.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setException( Exception exception )
+ {
+ this.exception = exception;
+ return this;
+ }
+
+ /**
+ * Builds a new transfer event from the current values of this builder. The state of the builder itself remains
+ * unchanged.
+ *
+ * @return The transfer event, never {@code null}.
+ */
+ public TransferEvent build()
+ {
+ return new TransferEvent( this );
+ }
+
+ }
+
+}