001package org.eclipse.aether.spi.connector.transport; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.Closeable; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.nio.ByteBuffer; 027import java.util.concurrent.atomic.AtomicBoolean; 028 029import org.eclipse.aether.transfer.TransferCancelledException; 030 031/** 032 * A skeleton implementation for custom transporters. 033 */ 034public abstract class AbstractTransporter 035 implements Transporter 036{ 037 038 private final AtomicBoolean closed; 039 040 /** 041 * Enables subclassing. 042 */ 043 protected AbstractTransporter() 044 { 045 closed = new AtomicBoolean(); 046 } 047 048 public void peek( PeekTask task ) 049 throws Exception 050 { 051 failIfClosed( task ); 052 implPeek( task ); 053 } 054 055 /** 056 * Implements {@link #peek(PeekTask)}, gets only called if the transporter has not been closed. 057 * 058 * @param task The existence check to perform, must not be {@code null}. 059 * @throws Exception If the existence of the specified resource could not be confirmed. 060 */ 061 protected abstract void implPeek( PeekTask task ) 062 throws Exception; 063 064 public void get( GetTask task ) 065 throws Exception 066 { 067 failIfClosed( task ); 068 implGet( task ); 069 } 070 071 /** 072 * Implements {@link #get(GetTask)}, gets only called if the transporter has not been closed. 073 * 074 * @param task The download to perform, must not be {@code null}. 075 * @throws Exception If the transfer failed. 076 */ 077 protected abstract void implGet( GetTask task ) 078 throws Exception; 079 080 /** 081 * Performs stream-based I/O for the specified download task and notifies the configured transport listener. 082 * Subclasses might want to invoke this utility method from within their {@link #implGet(GetTask)} to avoid 083 * boilerplate I/O code. 084 * 085 * @param task The download to perform, must not be {@code null}. 086 * @param is The input stream to download the data from, must not be {@code null}. 087 * @param close {@code true} if the supplied input stream should be automatically closed, {@code false} to leave the 088 * stream open. 089 * @param length The size in bytes of the downloaded resource or {@code -1} if unknown, not to be confused with the 090 * length of the supplied input stream which might be smaller if the download is resumed. 091 * @param resume {@code true} if the download resumes from {@link GetTask#getResumeOffset()}, {@code false} if the 092 * download starts at the first byte of the resource. 093 * @throws IOException If the transfer encountered an I/O error. 094 * @throws TransferCancelledException If the transfer was cancelled. 095 */ 096 protected void utilGet( GetTask task, InputStream is, boolean close, long length, boolean resume ) 097 throws IOException, TransferCancelledException 098 { 099 try 100 { 101 task.getListener().transportStarted( resume ? task.getResumeOffset() : 0, length ); 102 OutputStream os = task.newOutputStream( resume ); 103 try 104 { 105 copy( os, is, task.getListener() ); 106 os.close(); 107 } 108 finally 109 { 110 close( os ); 111 } 112 } 113 finally 114 { 115 if ( close ) 116 { 117 close( is ); 118 } 119 } 120 } 121 122 public void put( PutTask task ) 123 throws Exception 124 { 125 failIfClosed( task ); 126 implPut( task ); 127 } 128 129 /** 130 * Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed. 131 * 132 * @param task The upload to perform, must not be {@code null}. 133 * @throws Exception If the transfer failed. 134 */ 135 protected abstract void implPut( PutTask task ) 136 throws Exception; 137 138 /** 139 * Performs stream-based I/O for the specified upload task and notifies the configured transport listener. 140 * Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid 141 * boilerplate I/O code. 142 * 143 * @param task The upload to perform, must not be {@code null}. 144 * @param os The output stream to upload the data to, must not be {@code null}. 145 * @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave 146 * the stream open. 147 * @throws IOException If the transfer encountered an I/O error. 148 * @throws TransferCancelledException If the transfer was cancelled. 149 */ 150 protected void utilPut( PutTask task, OutputStream os, boolean close ) 151 throws IOException, TransferCancelledException 152 { 153 try 154 { 155 task.getListener().transportStarted( 0, task.getDataLength() ); 156 InputStream is = task.newInputStream(); 157 try 158 { 159 copy( os, is, task.getListener() ); 160 } 161 finally 162 { 163 close( is ); 164 } 165 if ( close ) 166 { 167 os.close(); 168 } 169 else 170 { 171 os.flush(); 172 } 173 } 174 finally 175 { 176 if ( close ) 177 { 178 close( os ); 179 } 180 } 181 } 182 183 public void close() 184 { 185 if ( closed.compareAndSet( false, true ) ) 186 { 187 implClose(); 188 } 189 } 190 191 /** 192 * Implements {@link #close()}, gets only called if the transporter has not already been closed. 193 */ 194 protected abstract void implClose(); 195 196 private void failIfClosed( TransportTask task ) 197 { 198 if ( closed.get() ) 199 { 200 throw new IllegalStateException( "transporter closed, cannot execute task " + task ); 201 } 202 } 203 204 private static void copy( OutputStream os, InputStream is, TransportListener listener ) 205 throws IOException, TransferCancelledException 206 { 207 ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 ); 208 byte[] array = buffer.array(); 209 for ( int read = is.read( array ); read >= 0; read = is.read( array ) ) 210 { 211 os.write( array, 0, read ); 212 buffer.rewind(); 213 buffer.limit( read ); 214 listener.transportProgressed( buffer ); 215 } 216 } 217 218 private static void close( Closeable file ) 219 { 220 if ( file != null ) 221 { 222 try 223 { 224 file.close(); 225 } 226 catch ( IOException e ) 227 { 228 // irrelevant 229 } 230 } 231 } 232 233}