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.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         OutputStream os = null;
104         try
105         {
106             os = task.newOutputStream( resume );
107             task.getListener().transportStarted( resume ? task.getResumeOffset() : 0L, length );
108             copy( os, is, task.getListener() );
109             os.close();
110             os = null;
111 
112             if ( close )
113             {
114                 is.close();
115                 is = null;
116             }
117         }
118         finally
119         {
120             try
121             {
122                 if ( os != null )
123                 {
124                     os.close();
125                 }
126             }
127             catch ( final IOException e )
128             {
129                 // Suppressed due to an exception already thrown in the try block.
130             }
131             finally
132             {
133                 try
134                 {
135                     if ( close && is != null )
136                     {
137                         is.close();
138                     }
139                 }
140                 catch ( final IOException e )
141                 {
142                     // Suppressed due to an exception already thrown in the try block.
143                 }
144             }
145         }
146     }
147 
148     public void put( PutTask task )
149         throws Exception
150     {
151         Objects.requireNonNull( task, "task cannot be null" );
152 
153         failIfClosed( task );
154         implPut( task );
155     }
156 
157     /**
158      * Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed.
159      *
160      * @param task The upload to perform, must not be {@code null}.
161      * @throws Exception If the transfer failed.
162      */
163     protected abstract void implPut( PutTask task )
164         throws Exception;
165 
166     /**
167      * Performs stream-based I/O for the specified upload task and notifies the configured transport listener.
168      * Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid
169      * boilerplate I/O code.
170      *
171      * @param task The upload to perform, must not be {@code null}.
172      * @param os The output stream to upload the data to, must not be {@code null}.
173      * @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave
174      *            the stream open.
175      * @throws IOException If the transfer encountered an I/O error.
176      * @throws TransferCancelledException If the transfer was cancelled.
177      */
178     protected void utilPut( PutTask task, OutputStream os, boolean close )
179         throws IOException, TransferCancelledException
180     {
181         InputStream is = null;
182         try
183         {
184             task.getListener().transportStarted( 0, task.getDataLength() );
185             is = task.newInputStream();
186             copy( os, is, task.getListener() );
187 
188             if ( close )
189             {
190                 os.close();
191             }
192             else
193             {
194                 os.flush();
195             }
196 
197             os = null;
198 
199             is.close();
200             is = null;
201         }
202         finally
203         {
204             try
205             {
206                 if ( close && os != null )
207                 {
208                     os.close();
209                 }
210             }
211             catch ( final IOException e )
212             {
213                 // Suppressed due to an exception already thrown in the try block.
214             }
215             finally
216             {
217                 try
218                 {
219                     if ( is != null )
220                     {
221                         is.close();
222                     }
223                 }
224                 catch ( final IOException e )
225                 {
226                     // Suppressed due to an exception already thrown in the try block.
227                 }
228             }
229         }
230     }
231 
232     public void close()
233     {
234         if ( closed.compareAndSet( false, true ) )
235         {
236             implClose();
237         }
238     }
239 
240     /**
241      * Implements {@link #close()}, gets only called if the transporter has not already been closed.
242      */
243     protected abstract void implClose();
244 
245     private void failIfClosed( TransportTask task )
246     {
247         if ( closed.get() )
248         {
249             throw new IllegalStateException( "transporter closed, cannot execute task " + task );
250         }
251     }
252 
253     private static void copy( OutputStream os, InputStream is, TransportListener listener )
254         throws IOException, TransferCancelledException
255     {
256         byte[] buffer = new byte[ 1024 * 32 ];
257         for ( int read = is.read( buffer ); read >= 0; read = is.read( buffer ) )
258         {
259             os.write( buffer, 0, read );
260             listener.transportProgressed( ByteBuffer.wrap( buffer, 0, read ) );
261         }
262     }
263 
264 }