View Javadoc
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.Buffer;
26  import java.nio.ByteBuffer;
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          failIfClosed( task );
52          implPeek( task );
53      }
54  
55      /**
56       * Implements {@link #peek(PeekTask)}, gets only called if the transporter has not been closed.
57       *
58       * @param task The existence check to perform, must not be {@code null}.
59       * @throws Exception If the existence of the specified resource could not be confirmed.
60       */
61      protected abstract void implPeek( PeekTask task )
62          throws Exception;
63  
64      public void get( GetTask task )
65          throws Exception
66      {
67          failIfClosed( task );
68          implGet( task );
69      }
70  
71      /**
72       * Implements {@link #get(GetTask)}, gets only called if the transporter has not been closed.
73       *
74       * @param task The download to perform, must not be {@code null}.
75       * @throws Exception If the transfer failed.
76       */
77      protected abstract void implGet( GetTask task )
78          throws Exception;
79  
80      /**
81       * Performs stream-based I/O for the specified download task and notifies the configured transport listener.
82       * Subclasses might want to invoke this utility method from within their {@link #implGet(GetTask)} to avoid
83       * boilerplate I/O code.
84       *
85       * @param task The download to perform, must not be {@code null}.
86       * @param is The input stream to download the data from, must not be {@code null}.
87       * @param close {@code true} if the supplied input stream should be automatically closed, {@code false} to leave the
88       *            stream open.
89       * @param length The size in bytes of the downloaded resource or {@code -1} if unknown, not to be confused with the
90       *            length of the supplied input stream which might be smaller if the download is resumed.
91       * @param resume {@code true} if the download resumes from {@link GetTask#getResumeOffset()}, {@code false} if the
92       *            download starts at the first byte of the resource.
93       * @throws IOException If the transfer encountered an I/O error.
94       * @throws TransferCancelledException If the transfer was cancelled.
95       */
96      protected void utilGet( GetTask task, InputStream is, boolean close, long length, boolean resume )
97          throws IOException, TransferCancelledException
98      {
99          OutputStream os = null;
100         try
101         {
102             os = task.newOutputStream( resume );
103             task.getListener().transportStarted( resume ? task.getResumeOffset() : 0L, length );
104             copy( os, is, task.getListener() );
105             os.close();
106             os = null;
107 
108             if ( close )
109             {
110                 is.close();
111                 is = null;
112             }
113         }
114         finally
115         {
116             try
117             {
118                 if ( os != null )
119                 {
120                     os.close();
121                 }
122             }
123             catch ( final IOException e )
124             {
125                 // Suppressed due to an exception already thrown in the try block.
126             }
127             finally
128             {
129                 try
130                 {
131                     if ( close && is != null )
132                     {
133                         is.close();
134                     }
135                 }
136                 catch ( final IOException e )
137                 {
138                     // Suppressed due to an exception already thrown in the try block.
139                 }
140             }
141         }
142     }
143 
144     public void put( PutTask task )
145         throws Exception
146     {
147         failIfClosed( task );
148         implPut( task );
149     }
150 
151     /**
152      * Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed.
153      *
154      * @param task The upload to perform, must not be {@code null}.
155      * @throws Exception If the transfer failed.
156      */
157     protected abstract void implPut( PutTask task )
158         throws Exception;
159 
160     /**
161      * Performs stream-based I/O for the specified upload task and notifies the configured transport listener.
162      * Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid
163      * boilerplate I/O code.
164      *
165      * @param task The upload to perform, must not be {@code null}.
166      * @param os The output stream to upload the data to, must not be {@code null}.
167      * @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave
168      *            the stream open.
169      * @throws IOException If the transfer encountered an I/O error.
170      * @throws TransferCancelledException If the transfer was cancelled.
171      */
172     protected void utilPut( PutTask task, OutputStream os, boolean close )
173         throws IOException, TransferCancelledException
174     {
175         InputStream is = null;
176         try
177         {
178             task.getListener().transportStarted( 0, task.getDataLength() );
179             is = task.newInputStream();
180             copy( os, is, task.getListener() );
181 
182             if ( close )
183             {
184                 os.close();
185             }
186             else
187             {
188                 os.flush();
189             }
190 
191             os = null;
192 
193             is.close();
194             is = null;
195         }
196         finally
197         {
198             try
199             {
200                 if ( close && os != null )
201                 {
202                     os.close();
203                 }
204             }
205             catch ( final IOException e )
206             {
207                 // Suppressed due to an exception already thrown in the try block.
208             }
209             finally
210             {
211                 try
212                 {
213                     if ( is != null )
214                     {
215                         is.close();
216                     }
217                 }
218                 catch ( final IOException e )
219                 {
220                     // Suppressed due to an exception already thrown in the try block.
221                 }
222             }
223         }
224     }
225 
226     public void close()
227     {
228         if ( closed.compareAndSet( false, true ) )
229         {
230             implClose();
231         }
232     }
233 
234     /**
235      * Implements {@link #close()}, gets only called if the transporter has not already been closed.
236      */
237     protected abstract void implClose();
238 
239     private void failIfClosed( TransportTask task )
240     {
241         if ( closed.get() )
242         {
243             throw new IllegalStateException( "transporter closed, cannot execute task " + task );
244         }
245     }
246 
247     private static void copy( OutputStream os, InputStream is, TransportListener listener )
248         throws IOException, TransferCancelledException
249     {
250         ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
251         byte[] array = buffer.array();
252         for ( int read = is.read( array ); read >= 0; read = is.read( array ) )
253         {
254             os.write( array, 0, read );
255             ( (Buffer) buffer ).rewind();
256             ( (Buffer) buffer ).limit( read );
257             listener.transportProgressed( buffer );
258         }
259     }
260 
261 }