1 package org.eclipse.aether.spi.connector.transport; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.OutputStream; 25 import java.nio.ByteBuffer; 26 import java.util.Objects; 27 import java.util.concurrent.atomic.AtomicBoolean; 28 29 import org.eclipse.aether.transfer.TransferCancelledException; 30 31 /** 32 * A skeleton implementation for custom transporters. 33 */ 34 public abstract class AbstractTransporter 35 implements Transporter 36 { 37 38 private final AtomicBoolean closed; 39 40 /** 41 * Enables subclassing. 42 */ 43 protected AbstractTransporter() 44 { 45 closed = new AtomicBoolean(); 46 } 47 48 public void peek( PeekTask task ) 49 throws Exception 50 { 51 Objects.requireNonNull( task, "task cannot be null" ); 52 53 failIfClosed( task ); 54 implPeek( task ); 55 } 56 57 /** 58 * Implements {@link #peek(PeekTask)}, gets only called if the transporter has not been closed. 59 * 60 * @param task The existence check to perform, must not be {@code null}. 61 * @throws Exception If the existence of the specified resource could not be confirmed. 62 */ 63 protected abstract void implPeek( PeekTask task ) 64 throws Exception; 65 66 public void get( GetTask task ) 67 throws Exception 68 { 69 Objects.requireNonNull( task, "task cannot be null" ); 70 71 failIfClosed( task ); 72 implGet( task ); 73 } 74 75 /** 76 * Implements {@link #get(GetTask)}, gets only called if the transporter has not been closed. 77 * 78 * @param task The download to perform, must not be {@code null}. 79 * @throws Exception If the transfer failed. 80 */ 81 protected abstract void implGet( GetTask task ) 82 throws Exception; 83 84 /** 85 * Performs stream-based I/O for the specified download task and notifies the configured transport listener. 86 * Subclasses might want to invoke this utility method from within their {@link #implGet(GetTask)} to avoid 87 * boilerplate I/O code. 88 * 89 * @param task The download to perform, must not be {@code null}. 90 * @param is The input stream to download the data from, must not be {@code null}. 91 * @param close {@code true} if the supplied input stream should be automatically closed, {@code false} to leave the 92 * stream open. 93 * @param length The size in bytes of the downloaded resource or {@code -1} if unknown, not to be confused with the 94 * length of the supplied input stream which might be smaller if the download is resumed. 95 * @param resume {@code true} if the download resumes from {@link GetTask#getResumeOffset()}, {@code false} if the 96 * download starts at the first byte of the resource. 97 * @throws IOException If the transfer encountered an I/O error. 98 * @throws TransferCancelledException If the transfer was cancelled. 99 */ 100 protected void utilGet( GetTask task, InputStream is, boolean close, long length, boolean resume ) 101 throws IOException, TransferCancelledException 102 { 103 try ( OutputStream os = task.newOutputStream( resume ) ) 104 { 105 task.getListener().transportStarted( resume ? task.getResumeOffset() : 0L, length ); 106 copy( os, is, task.getListener() ); 107 } 108 finally 109 { 110 if ( close ) 111 { 112 is.close(); 113 } 114 } 115 } 116 117 public void put( PutTask task ) 118 throws Exception 119 { 120 Objects.requireNonNull( task, "task cannot be null" ); 121 122 failIfClosed( task ); 123 implPut( task ); 124 } 125 126 /** 127 * Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed. 128 * 129 * @param task The upload to perform, must not be {@code null}. 130 * @throws Exception If the transfer failed. 131 */ 132 protected abstract void implPut( PutTask task ) 133 throws Exception; 134 135 /** 136 * Performs stream-based I/O for the specified upload task and notifies the configured transport listener. 137 * Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid 138 * boilerplate I/O code. 139 * 140 * @param task The upload to perform, must not be {@code null}. 141 * @param os The output stream to upload the data to, must not be {@code null}. 142 * @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave 143 * the stream open. 144 * @throws IOException If the transfer encountered an I/O error. 145 * @throws TransferCancelledException If the transfer was cancelled. 146 */ 147 protected void utilPut( PutTask task, OutputStream os, boolean close ) 148 throws IOException, TransferCancelledException 149 { 150 try ( InputStream is = task.newInputStream() ) 151 { 152 task.getListener().transportStarted( 0, task.getDataLength() ); 153 copy( os, is, task.getListener() ); 154 } 155 finally 156 { 157 if ( close ) 158 { 159 os.close(); 160 } 161 else 162 { 163 os.flush(); 164 } 165 } 166 } 167 168 public void close() 169 { 170 if ( closed.compareAndSet( false, true ) ) 171 { 172 implClose(); 173 } 174 } 175 176 /** 177 * Implements {@link #close()}, gets only called if the transporter has not already been closed. 178 */ 179 protected abstract void implClose(); 180 181 private void failIfClosed( TransportTask task ) 182 { 183 if ( closed.get() ) 184 { 185 throw new IllegalStateException( "transporter closed, cannot execute task " + task ); 186 } 187 } 188 189 private static void copy( OutputStream os, InputStream is, TransportListener listener ) 190 throws IOException, TransferCancelledException 191 { 192 byte[] buffer = new byte[ 1024 * 32 ]; 193 for ( int read = is.read( buffer ); read >= 0; read = is.read( buffer ) ) 194 { 195 os.write( buffer, 0, read ); 196 listener.transportProgressed( ByteBuffer.wrap( buffer, 0, read ) ); 197 } 198 } 199 200 }