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         try ( OutputStream os = task.newOutputStream( resume ) )
104         {
105             task.getListener().transportStarted( resume ? task.getResumeOffset() : 0L, length );
106             copy( os, is, task.getListener() );
107         }
108         finally
109         {
110             if ( close )
111             {
112                 is.close();
113             }
114         }
115     }
116 
117     public void put( PutTask task )
118         throws Exception
119     {
120         Objects.requireNonNull( task, "task cannot be null" );
121 
122         failIfClosed( task );
123         implPut( task );
124     }
125 
126     /**
127      * Implements {@link #put(PutTask)}, gets only called if the transporter has not been closed.
128      *
129      * @param task The upload to perform, must not be {@code null}.
130      * @throws Exception If the transfer failed.
131      */
132     protected abstract void implPut( PutTask task )
133         throws Exception;
134 
135     /**
136      * Performs stream-based I/O for the specified upload task and notifies the configured transport listener.
137      * Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid
138      * boilerplate I/O code.
139      *
140      * @param task The upload to perform, must not be {@code null}.
141      * @param os The output stream to upload the data to, must not be {@code null}.
142      * @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave
143      *            the stream open.
144      * @throws IOException If the transfer encountered an I/O error.
145      * @throws TransferCancelledException If the transfer was cancelled.
146      */
147     protected void utilPut( PutTask task, OutputStream os, boolean close )
148         throws IOException, TransferCancelledException
149     {
150         try ( InputStream is = task.newInputStream() )
151         {
152             task.getListener().transportStarted( 0, task.getDataLength() );
153             copy( os, is, task.getListener() );
154         }
155         finally
156         {
157             if ( close )
158             {
159                 os.close();
160             }
161             else
162             {
163                 os.flush();
164             }
165         }
166     }
167 
168     public void close()
169     {
170         if ( closed.compareAndSet( false, true ) )
171         {
172             implClose();
173         }
174     }
175 
176     /**
177      * Implements {@link #close()}, gets only called if the transporter has not already been closed.
178      */
179     protected abstract void implClose();
180 
181     private void failIfClosed( TransportTask task )
182     {
183         if ( closed.get() )
184         {
185             throw new IllegalStateException( "transporter closed, cannot execute task " + task );
186         }
187     }
188 
189     private static void copy( OutputStream os, InputStream is, TransportListener listener )
190         throws IOException, TransferCancelledException
191     {
192         byte[] buffer = new byte[ 1024 * 32 ];
193         for ( int read = is.read( buffer ); read >= 0; read = is.read( buffer ) )
194         {
195             os.write( buffer, 0, read );
196             listener.transportProgressed( ByteBuffer.wrap( buffer, 0, read ) );
197         }
198     }
199 
200 }