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