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