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 }