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