blob: acad985c9347ac531e27b04478bac4fa447b6564 [file] [log] [blame]
package org.eclipse.aether.spi.connector.transport;
/*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.aether.transfer.TransferCancelledException;
/**
* A skeleton implementation for custom transporters.
*/
public abstract class AbstractTransporter
implements Transporter
{
private final AtomicBoolean closed;
/**
* Enables subclassing.
*/
protected AbstractTransporter()
{
closed = new AtomicBoolean();
}
public void peek( PeekTask task )
throws Exception
{
failIfClosed( task );
implPeek( task );
}
/**
* Implements {@link #peek(PeekTask)}, gets only called if the transporter has not been closed.
*
* @param task The existence check to perform, must not be {@code null}.
* @throws Exception If the existence of the specified resource could not be confirmed.
*/
protected abstract void implPeek( PeekTask task )
throws Exception;
public void get( GetTask task )
throws Exception
{
failIfClosed( task );
implGet( task );
}
/**
* Implements {@link #get(GetTask)}, gets only called if the transporter has not been closed.
*
* @param task The download to perform, must not be {@code null}.
* @throws Exception If the transfer failed.
*/
protected abstract void implGet( GetTask task )
throws Exception;
/**
* Performs stream-based I/O for the specified download task and notifies the configured transport listener.
* Subclasses might want to invoke this utility method from within their {@link #implGet(GetTask)} to avoid
* boilerplate I/O code.
*
* @param task The download to perform, must not be {@code null}.
* @param is The input stream to download the data from, must not be {@code null}.
* @param close {@code true} if the supplied input stream should be automatically closed, {@code false} to leave the
* stream open.
* @param length The size in bytes of the downloaded resource or {@code -1} if unknown, not to be confused with the
* length of the supplied input stream which might be smaller if the download is resumed.
* @param resume {@code true} if the download resumes from {@link GetTask#getResumeOffset()}, {@code false} if the
* download starts at the first byte of the resource.
* @throws IOException If the transfer encountered an I/O error.
* @throws TransferCancelledException If the transfer was cancelled.
*/
protected void utilGet( GetTask task, InputStream is, boolean close, long length, boolean resume )
throws IOException, TransferCancelledException
{
OutputStream os = null;
try
{
os = task.newOutputStream( resume );
task.getListener().transportStarted( resume ? task.getResumeOffset() : 0, length );
copy( os, is, task.getListener() );
os.close();
os = null;
if ( close )
{
is.close();
is = null;
}
}
finally
{
try
{
if ( os != null )
{
os.close();
}
}
catch ( final IOException e )
{
// Suppressed
}
finally
{
try
{
if ( close && is != null )
{
is.close();
}
}
catch ( final IOException e )
{
// Suppressed
}
}
}
}
public void put( PutTask task )
throws Exception
{
failIfClosed( task );
implPut( task );
}
/**
* Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed.
*
* @param task The upload to perform, must not be {@code null}.
* @throws Exception If the transfer failed.
*/
protected abstract void implPut( PutTask task )
throws Exception;
/**
* Performs stream-based I/O for the specified upload task and notifies the configured transport listener.
* Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid
* boilerplate I/O code.
*
* @param task The upload to perform, must not be {@code null}.
* @param os The output stream to upload the data to, must not be {@code null}.
* @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave
* the stream open.
* @throws IOException If the transfer encountered an I/O error.
* @throws TransferCancelledException If the transfer was cancelled.
*/
protected void utilPut( PutTask task, OutputStream out, boolean close )
throws IOException, TransferCancelledException
{
InputStream in = null;
try
{
in = task.newInputStream();
task.getListener().transportStarted( 0, task.getDataLength() );
copy( out, in, task.getListener() );
if ( close )
{
out.close();
}
else
{
out.flush();
}
out = null;
in.close();
in = null;
}
finally
{
try
{
if ( close && out != null )
{
out.close();
}
}
catch ( final IOException e )
{
// Suppressed
}
finally
{
try
{
if ( in != null )
{
in.close();
}
}
catch ( final IOException e )
{
// Suppressed
}
}
}
}
public void close()
{
if ( closed.compareAndSet( false, true ) )
{
implClose();
}
}
/**
* Implements {@link #close()}, gets only called if the transporter has not already been closed.
*/
protected abstract void implClose();
private void failIfClosed( TransportTask task )
{
if ( closed.get() )
{
throw new IllegalStateException( "transporter closed, cannot execute task " + task );
}
}
private static void copy( OutputStream os, InputStream is, TransportListener listener )
throws IOException, TransferCancelledException
{
ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
byte[] array = buffer.array();
for ( int read = is.read( array ); read >= 0; read = is.read( array ) )
{
os.write( array, 0, read );
buffer.rewind();
buffer.limit( read );
listener.transportProgressed( buffer );
}
}
}