5
5
import okhttp3 .MediaType ;
6
6
import okhttp3 .MultipartBody ;
7
7
import okhttp3 .OkHttpClient ;
8
+ import okhttp3 .Protocol ;
8
9
import okhttp3 .Request ;
9
10
import okhttp3 .RequestBody ;
10
11
import okhttp3 .Response ;
15
16
import org .checkerframework .checker .nullness .qual .NonNull ;
16
17
17
18
import javax .annotation .Nullable ;
19
+
18
20
import java .io .File ;
19
21
import java .io .IOException ;
20
22
import java .io .InputStream ;
21
23
import java .net .SocketTimeoutException ;
22
24
import java .nio .file .Files ;
23
25
import java .time .Duration ;
26
+ import java .util .Arrays ;
24
27
import java .util .concurrent .TimeUnit ;
25
28
import java .util .logging .Level ;
26
29
import java .util .logging .Logger ;
31
34
import static java .util .Objects .requireNonNull ;
32
35
import static java .util .concurrent .TimeUnit .MILLISECONDS ;
33
36
34
- public class DatabendPresignClientV1 implements DatabendPresignClient {
37
+ public class DatabendPresignClientV1
38
+ implements DatabendPresignClient
39
+ {
35
40
36
41
private static final int MaxRetryAttempts = 20 ;
37
42
@@ -40,15 +45,52 @@ public class DatabendPresignClientV1 implements DatabendPresignClient {
40
45
private final String uri ;
41
46
private static final Logger logger = Logger .getLogger (DatabendPresignClientV1 .class .getPackage ().getName ());
42
47
43
- public DatabendPresignClientV1 (OkHttpClient client , String uri ) {
48
+ public DatabendPresignClientV1 (OkHttpClient client , String uri )
49
+ {
44
50
Logger .getLogger (OkHttpClient .class .getName ()).setLevel (Level .FINEST );
45
51
OkHttpClient .Builder builder = client .newBuilder ();
46
52
this .client = builder .
47
- connectTimeout (120 , TimeUnit .SECONDS ).build ();
53
+ connectTimeout (600 , TimeUnit .SECONDS )
54
+ .writeTimeout (900 , TimeUnit .SECONDS )
55
+ .readTimeout (600 , TimeUnit .SECONDS )
56
+ .retryOnConnectionFailure (true )
57
+ .protocols (Arrays .asList (Protocol .HTTP_1_1 ))
58
+ .addInterceptor (chain -> {
59
+ Request request = chain .request ();
60
+ int retryCount = 0 ;
61
+ Response response = null ;
62
+ while (retryCount < 3 ) {
63
+ try {
64
+ response = chain .proceed (request );
65
+ if (response .isSuccessful ()) {
66
+ return response ;
67
+ }
68
+ response .close ();
69
+ }
70
+ catch (IOException e ) {
71
+ if (retryCount == 2 ) {
72
+ throw e ;
73
+ }
74
+ }
75
+ retryCount ++;
76
+
77
+ long waitTimeMs = (long ) (Math .pow (2 , retryCount ) * 1000 );
78
+ try {
79
+ TimeUnit .MILLISECONDS .sleep (waitTimeMs );
80
+ }
81
+ catch (InterruptedException e ) {
82
+ Thread .currentThread ().interrupt ();
83
+ throw new IOException ("Upload interrupted" , e );
84
+ }
85
+ }
86
+ return response ;
87
+ }).build ();
48
88
this .uri = uri ;
49
89
}
50
90
51
- private void uploadFromStream (InputStream inputStream , String stageName , String relativePath , String name , long fileSize ) throws IOException {
91
+ private void uploadFromStream (InputStream inputStream , String stageName , String relativePath , String name , long fileSize )
92
+ throws IOException
93
+ {
52
94
// multipart upload input stream into /v1/upload_to_stage
53
95
RequestBody requestBody = new MultipartBody .Builder ()
54
96
.setType (MultipartBody .FORM )
@@ -73,24 +115,31 @@ private void uploadFromStream(InputStream inputStream, String stageName, String
73
115
.build ();
74
116
try {
75
117
executeInternal (request , true );
76
-
77
- } catch (IOException e ) {
118
+ }
119
+ catch (IOException e ) {
78
120
throw new IOException ("uploadFromStreamAPI failed" , e );
79
121
}
80
-
81
122
}
82
123
83
- private void uploadFromStream (InputStream inputStream , Headers headers , String presignedUrl , long fileSize ) throws IOException {
84
- requireNonNull (inputStream , "inputStream is null" );
85
- Request r = putRequest (headers , presignedUrl , inputStream , fileSize );
124
+ private void uploadFromStream (InputStream inputStream , Headers headers , String presignedUrl , long fileSize )
125
+ throws IOException
126
+ {
127
+ logger .info ("Starting upload: size=" + fileSize + " bytes, url=" + presignedUrl );
128
+ long startTime = System .currentTimeMillis ();
86
129
try {
130
+ Request r = putRequest (headers , presignedUrl , inputStream , fileSize );
87
131
executeInternal (r , true );
88
- } catch (IOException e ) {
89
- throw new IOException (format (" uploadFromStream failed, file size is %s kb, error is %s" , fileSize / 1024.0 , presignedUrl , e .toString ()));
132
+ logger .info ("Upload completed in " + (System .currentTimeMillis () - startTime ) + "ms" );
133
+ }
134
+ catch (IOException e ) {
135
+ logger .severe ("Upload failed after " + (System .currentTimeMillis () - startTime ) + "ms: " + e .getMessage ());
136
+ throw e ;
90
137
}
91
138
}
92
139
93
- private ResponseBody executeInternal (Request request , boolean shouldClose ) throws IOException {
140
+ private ResponseBody executeInternal (Request request , boolean shouldClose )
141
+ throws IOException
142
+ {
94
143
requireNonNull (request , "request is null" );
95
144
long start = System .nanoTime ();
96
145
long attempts = 0 ;
@@ -110,9 +159,11 @@ private ResponseBody executeInternal(Request request, boolean shouldClose) throw
110
159
111
160
try {
112
161
MILLISECONDS .sleep (attempts * 100 );
113
- } catch (InterruptedException e ) {
162
+ }
163
+ catch (InterruptedException e ) {
114
164
try {
115
- } finally {
165
+ }
166
+ finally {
116
167
Thread .currentThread ().interrupt ();
117
168
}
118
169
throw new RuntimeException ("StatementClient thread was interrupted" );
@@ -124,122 +175,169 @@ private ResponseBody executeInternal(Request request, boolean shouldClose) throw
124
175
response = client .newCall (request ).execute ();
125
176
if (response .isSuccessful ()) {
126
177
return response .body ();
127
- } else if (response .code () == 401 ) {
178
+ }
179
+ else if (response .code () == 401 ) {
128
180
throw new RuntimeException ("Error exeucte presign, Unauthorized user: " + response .code () + " " + response .message ());
129
- } else if (response .code () >= 503 ) {
181
+ }
182
+ else if (response .code () >= 503 ) {
130
183
cause = new RuntimeException ("Error execute presign, service unavailable: " + response .code () + " " + response .message ());
131
- } else if (response .code () >= 400 ) {
184
+ }
185
+ else if (response .code () >= 400 ) {
132
186
cause = new RuntimeException ("Error execute presign, configuration error: " + response .code () + " " + response .message ());
133
187
}
134
- } catch (SocketTimeoutException e ) {
188
+ }
189
+ catch (SocketTimeoutException e ) {
135
190
logger .warning ("Error execute presign, socket timeout: " + e .getMessage ());
136
191
cause = new RuntimeException ("Error execute presign, request is " + request .toString () + "socket timeout: " + e .getMessage ());
137
- } catch (RuntimeException e ) {
192
+ }
193
+ catch (RuntimeException e ) {
138
194
cause = e ;
139
- } finally {
195
+ }
196
+ finally {
140
197
if (shouldClose ) {
141
198
try {
142
199
if (response != null ) {
143
200
response .close ();
144
201
}
145
- } catch (Exception e ) {
202
+ }
203
+ catch (Exception e ) {
146
204
// ignore
147
205
}
148
206
}
149
207
}
150
-
151
208
}
152
209
}
153
210
154
211
@ Override
155
212
public void presignUpload (File srcFile , InputStream inputStream , Headers headers ,
156
- String presignedUrl , long fileSize , boolean uploadFromStream ) throws IOException {
213
+ String presignedUrl , long fileSize , boolean uploadFromStream )
214
+ throws IOException
215
+ {
157
216
158
217
InputStream it = null ;
159
218
if (!uploadFromStream ) {
160
219
it = Files .newInputStream (srcFile .toPath ());
161
- } else {
220
+ }
221
+ else {
162
222
it = inputStream ;
163
223
}
164
224
uploadFromStream (it , headers , presignedUrl , fileSize );
165
225
}
166
226
167
227
@ Override
168
- public void presignUpload (File srcFile , InputStream inputStream , String stageName , String relativePath , String name , long fileSize , boolean uploadFromStream ) throws IOException {
228
+ public void presignUpload (File srcFile , InputStream inputStream , String stageName , String relativePath , String name , long fileSize , boolean uploadFromStream )
229
+ throws IOException
230
+ {
169
231
if (!uploadFromStream ) {
170
232
try (InputStream it = Files .newInputStream (srcFile .toPath ())) {
171
233
uploadFromStream (it , stageName , relativePath , name , fileSize );
172
234
}
173
- } else {
235
+ }
236
+ else {
174
237
uploadFromStream (inputStream , stageName , relativePath , name , fileSize );
175
238
}
176
239
}
177
240
178
241
@ Override
179
- public void presignDownload (String destFileName , Headers headers , String presignedUrl ) {
242
+ public void presignDownload (String destFileName , Headers headers , String presignedUrl )
243
+ {
180
244
Request r = getRequest (headers , presignedUrl );
181
245
try (ResponseBody body = executeInternal (r , false )) {
182
246
BufferedSink sink = Okio .buffer (Okio .sink (new File (destFileName )));
183
247
sink .writeAll (body .source ());
184
248
sink .close ();
185
- } catch (IOException e ) {
249
+ }
250
+ catch (IOException e ) {
186
251
throw new RuntimeException ("presignDownload failed" , e );
187
252
}
188
253
}
189
254
190
255
@ Override
191
- public InputStream presignDownloadStream (Headers headers , String presignedUrl ) {
256
+ public InputStream presignDownloadStream (Headers headers , String presignedUrl )
257
+ {
192
258
Request r = getRequest (headers , presignedUrl );
193
259
try {
194
260
ResponseBody responseBody = executeInternal (r , false );
195
261
return responseBody .byteStream ();
196
- } catch (IOException e ) {
262
+ }
263
+ catch (IOException e ) {
197
264
throw new RuntimeException ("presignDownloadStream failed" , e );
198
265
}
199
266
}
200
267
201
- private Request getRequest (Headers headers , String url ) {
268
+ private Request getRequest (Headers headers , String url )
269
+ {
202
270
return new Request .Builder ().headers (headers ).url (url ).get ().build ();
203
271
}
204
272
205
- private Request putRequest (Headers headers , String url , InputStream inputStream , long fileSize )
206
- throws IOException {
207
- RequestBody input = new InputStreamRequestBody (null , inputStream , fileSize );
208
- return new Request .Builder ().headers (headers ).url (url ).put (input ).build ();
209
- }
273
+ private Request putRequest (Headers headers , String presignedUrl , InputStream inputStream , long fileSize ) {
274
+ RequestBody requestBody = new RequestBody () {
275
+ @ Override
276
+ public MediaType contentType () {
277
+ return MediaType .parse ("application/octet-stream" );
278
+ }
279
+
280
+ @ Override
281
+ public long contentLength () {
282
+ return fileSize ;
283
+ }
210
284
285
+ @ Override
286
+ public void writeTo (BufferedSink sink ) throws IOException {
287
+ try (Source source = Okio .source (inputStream )) {
288
+ sink .writeAll (source );
289
+ }
290
+ }
291
+ };
292
+
293
+ return new Request .Builder ()
294
+ .url (presignedUrl )
295
+ .put (requestBody )
296
+ .headers (headers )
297
+ .build ();
298
+ }
211
299
}
212
300
213
- class InputStreamRequestBody extends RequestBody {
301
+ class InputStreamRequestBody
302
+ extends RequestBody
303
+ {
214
304
private final InputStream inputStream ;
215
305
private final MediaType contentType ;
216
306
private final long fileSize ;
217
307
private static final Logger logger = Logger .getLogger (InputStreamRequestBody .class .getPackage ().getName ());
218
308
219
- public InputStreamRequestBody (MediaType contentType , InputStream inputStream , long fileSize ) {
220
- if (inputStream == null ) throw new NullPointerException ("inputStream == null" );
309
+ public InputStreamRequestBody (MediaType contentType , InputStream inputStream , long fileSize )
310
+ {
311
+ if (inputStream == null ) {
312
+ throw new NullPointerException ("inputStream == null" );
313
+ }
221
314
this .contentType = contentType ;
222
315
this .inputStream = inputStream ;
223
316
this .fileSize = fileSize ;
224
317
}
225
318
226
319
@ Nullable
227
320
@ Override
228
- public MediaType contentType () {
321
+ public MediaType contentType ()
322
+ {
229
323
return contentType ;
230
324
}
231
325
232
326
@ Override
233
- public long contentLength () {
327
+ public long contentLength ()
328
+ {
234
329
return fileSize ; // return the actual file size
235
330
// return inputStream.available() == 0 ? -1 : inputStream.available();
236
331
}
237
332
238
333
@ Override
239
- public void writeTo (@ NonNull BufferedSink sink ) throws IOException {
334
+ public void writeTo (@ NonNull BufferedSink sink )
335
+ throws IOException
336
+ {
240
337
try (Source source = Okio .source (inputStream )) {
241
338
sink .writeAll (source );
242
- } catch (IOException e ) {
339
+ }
340
+ catch (IOException e ) {
243
341
logger .warning (format ("writeTo failed, error is %s, cause is %s" , e .getMessage (), e .getCause ()));
244
342
}
245
343
}
0 commit comments