16
16
17
17
package com .google .cloud .storage ;
18
18
19
+ import static com .google .cloud .storage .Utils .ifNonNull ;
20
+
19
21
import com .google .api .client .http .HttpHeaders ;
20
22
import com .google .api .client .http .HttpResponse ;
21
23
import com .google .api .client .http .HttpResponseException ;
24
+ import com .google .api .gax .grpc .GrpcCallContext ;
25
+ import com .google .api .gax .rpc .ApiException ;
22
26
import com .google .cloud .BaseServiceException ;
23
27
import com .google .cloud .storage .StorageException .IOExceptionCallable ;
24
28
import com .google .common .io .CharStreams ;
29
+ import com .google .protobuf .MessageOrBuilder ;
30
+ import com .google .storage .v2 .ChecksummedData ;
31
+ import com .google .storage .v2 .ObjectChecksums ;
32
+ import com .google .storage .v2 .WriteObjectRequest ;
33
+ import com .google .storage .v2 .WriteObjectResponse ;
34
+ import io .grpc .StatusRuntimeException ;
25
35
import java .io .IOException ;
26
36
import java .io .InputStreamReader ;
27
37
import java .util .List ;
28
38
import java .util .Locale ;
29
39
import java .util .Map ;
40
+ import java .util .function .Consumer ;
30
41
import java .util .function .Predicate ;
31
42
import javax .annotation .ParametersAreNonnullByDefault ;
43
+ import org .checkerframework .checker .nullness .qual .NonNull ;
32
44
import org .checkerframework .checker .nullness .qual .Nullable ;
33
45
34
46
@ ParametersAreNonnullByDefault
@@ -69,6 +81,10 @@ enum ResumableSessionFailureScenario {
69
81
private static final String PREFIX_I = "\t |< " ;
70
82
private static final String PREFIX_O = "\t |> " ;
71
83
private static final String PREFIX_X = "\t | " ;
84
+ // define some constants for tab widths that are more compressed that the literals
85
+ private static final String T1 = "\t " ;
86
+ private static final String T2 = "\t \t " ;
87
+ private static final String T3 = "\t \t \t " ;
72
88
73
89
private static final Predicate <String > includedHeaders =
74
90
matches ("Content-Length" )
@@ -78,6 +94,7 @@ enum ResumableSessionFailureScenario {
78
94
.or (matches ("Range" ))
79
95
.or (startsWith ("X-Goog-Stored-" ))
80
96
.or (matches ("X-Goog-GCS-Idempotency-Token" ))
97
+ .or (matches ("X-Goog-request-params" ))
81
98
.or (matches ("X-GUploader-UploadID" ));
82
99
83
100
private static final Predicate <Map .Entry <String , ?>> includeHeader =
@@ -116,8 +133,12 @@ StorageException toStorageException(
116
133
return toStorageException (code , message , reason , uploadId , resp , cause , contentCallable );
117
134
}
118
135
119
- StorageException toStorageException () {
120
- return new StorageException (code , message , reason , null );
136
+ StorageException toStorageException (
137
+ @ NonNull List <@ NonNull WriteObjectRequest > reqs ,
138
+ @ Nullable WriteObjectResponse resp ,
139
+ @ NonNull GrpcCallContext context ,
140
+ @ Nullable Throwable cause ) {
141
+ return toStorageException (code , message , reason , reqs , resp , context , cause );
121
142
}
122
143
123
144
static StorageException toStorageException (
@@ -136,6 +157,102 @@ static StorageException toStorageException(
136
157
return se ;
137
158
}
138
159
160
+ static StorageException toStorageException (
161
+ int code ,
162
+ String message ,
163
+ @ Nullable String reason ,
164
+ @ NonNull List <@ NonNull WriteObjectRequest > reqs ,
165
+ @ Nullable WriteObjectResponse resp ,
166
+ @ NonNull GrpcCallContext context ,
167
+ @ Nullable Throwable cause ) {
168
+ final StringBuilder sb = new StringBuilder ();
169
+ sb .append (message );
170
+ // request context
171
+ Map <String , List <String >> extraHeaders = context .getExtraHeaders ();
172
+ recordHeadersTo (extraHeaders , PREFIX_O , sb );
173
+ int length = reqs .size ();
174
+ for (int i = 0 ; i < length ; i ++) {
175
+ if (i == 0 ) {
176
+ sb .append ("\n " ).append (PREFIX_O ).append ("[" );
177
+ } else {
178
+ sb .append ("," );
179
+ }
180
+ WriteObjectRequest req = reqs .get (i );
181
+ sb .append ("\n " ).append (PREFIX_O ).append (T1 ).append (req .getClass ().getName ()).append ("{" );
182
+ if (req .hasUploadId ()) {
183
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 ).append ("upload_id: " ).append (req .getUploadId ());
184
+ }
185
+ long writeOffset = req .getWriteOffset ();
186
+ if (req .hasChecksummedData ()) {
187
+ ChecksummedData checksummedData = req .getChecksummedData ();
188
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 );
189
+ sb .append (
190
+ String .format (
191
+ "checksummed_data: {range: [%d:%d]" ,
192
+ writeOffset , writeOffset + checksummedData .getContent ().size ()));
193
+ if (checksummedData .hasCrc32C ()) {
194
+ sb .append (", crc32c: " ).append (checksummedData .getCrc32C ());
195
+ }
196
+ sb .append ("}" );
197
+ } else {
198
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 ).append ("write_offset: " ).append (writeOffset );
199
+ }
200
+ if (req .getFinishWrite ()) {
201
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 ).append ("finish_write: true" );
202
+ }
203
+ if (req .hasObjectChecksums ()) {
204
+ ObjectChecksums objectChecksums = req .getObjectChecksums ();
205
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 ).append ("object_checksums: " ).append ("{" );
206
+ fmt (objectChecksums , PREFIX_O , T3 , sb );
207
+ sb .append ("\n " ).append (PREFIX_O ).append (T2 ).append ("}" );
208
+ }
209
+ sb .append ("\n " ).append (PREFIX_O ).append ("\t }" );
210
+ if (i == length - 1 ) {
211
+ sb .append ("\n " ).append (PREFIX_O ).append ("]" );
212
+ }
213
+ }
214
+
215
+ sb .append ("\n " ).append (PREFIX_X );
216
+
217
+ // response context
218
+ if (resp != null ) {
219
+ sb .append ("\n " ).append (PREFIX_I ).append (resp .getClass ().getName ()).append ("{" );
220
+ fmt (resp , PREFIX_I , T1 , sb );
221
+ sb .append ("\n " ).append (PREFIX_I ).append ("}" );
222
+ sb .append ("\n " ).append (PREFIX_X );
223
+ }
224
+
225
+ if (cause != null ) {
226
+ if (cause instanceof ApiException ) {
227
+ ApiException apiException = (ApiException ) cause ;
228
+ Throwable cause1 = apiException .getCause ();
229
+ if (cause1 instanceof StatusRuntimeException ) {
230
+ StatusRuntimeException statusRuntimeException = (StatusRuntimeException ) cause1 ;
231
+ sb .append ("\n " ).append (PREFIX_I ).append (statusRuntimeException .getStatus ());
232
+ ifNonNull (
233
+ statusRuntimeException .getTrailers (),
234
+ t -> sb .append ("\n " ).append (PREFIX_I ).append (t ));
235
+ } else {
236
+ sb .append ("\n " )
237
+ .append (PREFIX_I )
238
+ .append ("code: " )
239
+ .append (apiException .getStatusCode ().toString ());
240
+ ifNonNull (
241
+ apiException .getReason (),
242
+ r -> sb .append ("\n " ).append (PREFIX_I ).append ("reason: " ).append (r ));
243
+ ifNonNull (
244
+ apiException .getDomain (),
245
+ d -> sb .append ("\n " ).append (PREFIX_I ).append ("domain: " ).append (d ));
246
+ ifNonNull (
247
+ apiException .getErrorDetails (),
248
+ e -> sb .append ("\n " ).append (PREFIX_I ).append ("errorDetails: " ).append (e ));
249
+ }
250
+ sb .append ("\n " ).append (PREFIX_X );
251
+ }
252
+ }
253
+ return new StorageException (code , sb .toString (), reason , cause );
254
+ }
255
+
139
256
static StorageException toStorageException (
140
257
int overrideCode ,
141
258
String message ,
@@ -213,14 +330,21 @@ private static Predicate<String> startsWith(String prefix) {
213
330
}
214
331
215
332
private static void recordHeaderTo (HttpHeaders h , String prefix , StringBuilder sb ) {
216
- h .entrySet ().stream ()
217
- .filter (includeHeader )
218
- .forEach (
219
- e -> {
220
- String key = e .getKey ();
221
- String value = headerValueToString (e .getValue ());
222
- sb .append ("\n " ).append (prefix ).append (key ).append (": " ).append (value );
223
- });
333
+ h .entrySet ().stream ().filter (includeHeader ).forEach (writeHeaderValue (prefix , sb ));
334
+ }
335
+
336
+ private static void recordHeadersTo (
337
+ Map <String , List <String >> headers , String prefix , StringBuilder sb ) {
338
+ headers .entrySet ().stream ().filter (includeHeader ).forEach (writeHeaderValue (prefix , sb ));
339
+ }
340
+
341
+ private static <V > Consumer <Map .Entry <String , V >> writeHeaderValue (
342
+ String prefix , StringBuilder sb ) {
343
+ return e -> {
344
+ String key = e .getKey ();
345
+ String value = headerValueToString (e .getValue ());
346
+ sb .append ("\n " ).append (prefix ).append (key ).append (": " ).append (value );
347
+ };
224
348
}
225
349
226
350
private static String headerValueToString (Object o ) {
@@ -233,4 +357,18 @@ private static String headerValueToString(Object o) {
233
357
234
358
return o .toString ();
235
359
}
360
+
361
+ private static void fmt (
362
+ MessageOrBuilder msg ,
363
+ @ SuppressWarnings ("SameParameterValue" ) String prefix ,
364
+ String indentation ,
365
+ StringBuilder sb ) {
366
+ String string = msg .toString ();
367
+ // drop the final new line before prefixing
368
+ string = string .replaceAll ("\n $" , "" );
369
+ sb .append ("\n " )
370
+ .append (prefix )
371
+ .append (indentation )
372
+ .append (string .replaceAll ("\r ?\n " , "\n " + prefix + indentation ));
373
+ }
236
374
}
0 commit comments