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.Closeable;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
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          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 }