| 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 ); |
| } |
| } |
| |
| } |