blob: e2d98dad6f38e4ae605bb3be8e438f15154103f2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
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 );
}
}
}