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