1616
1717package com .google .cloud .storage ;
1818
19+ import static com .google .cloud .storage .Utils .ifNonNull ;
20+
1921import com .google .api .client .http .HttpHeaders ;
2022import com .google .api .client .http .HttpResponse ;
2123import com .google .api .client .http .HttpResponseException ;
24+ import com .google .api .gax .grpc .GrpcCallContext ;
25+ import com .google .api .gax .rpc .ApiException ;
2226import com .google .cloud .BaseServiceException ;
2327import com .google .cloud .storage .StorageException .IOExceptionCallable ;
2428import 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 ;
2535import java .io .IOException ;
2636import java .io .InputStreamReader ;
2737import java .util .List ;
2838import java .util .Locale ;
2939import java .util .Map ;
40+ import java .util .function .Consumer ;
3041import java .util .function .Predicate ;
3142import javax .annotation .ParametersAreNonnullByDefault ;
43+ import org .checkerframework .checker .nullness .qual .NonNull ;
3244import org .checkerframework .checker .nullness .qual .Nullable ;
3345
3446@ ParametersAreNonnullByDefault
@@ -69,6 +81,10 @@ enum ResumableSessionFailureScenario {
6981 private static final String PREFIX_I = "\t |< " ;
7082 private static final String PREFIX_O = "\t |> " ;
7183 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 " ;
7288
7389 private static final Predicate <String > includedHeaders =
7490 matches ("Content-Length" )
@@ -78,6 +94,7 @@ enum ResumableSessionFailureScenario {
7894 .or (matches ("Range" ))
7995 .or (startsWith ("X-Goog-Stored-" ))
8096 .or (matches ("X-Goog-GCS-Idempotency-Token" ))
97+ .or (matches ("X-Goog-request-params" ))
8198 .or (matches ("X-GUploader-UploadID" ));
8299
83100 private static final Predicate <Map .Entry <String , ?>> includeHeader =
@@ -116,8 +133,12 @@ StorageException toStorageException(
116133 return toStorageException (code , message , reason , uploadId , resp , cause , contentCallable );
117134 }
118135
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 );
121142 }
122143
123144 static StorageException toStorageException (
@@ -136,6 +157,102 @@ static StorageException toStorageException(
136157 return se ;
137158 }
138159
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+
139256 static StorageException toStorageException (
140257 int overrideCode ,
141258 String message ,
@@ -213,14 +330,21 @@ private static Predicate<String> startsWith(String prefix) {
213330 }
214331
215332 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+ };
224348 }
225349
226350 private static String headerValueToString (Object o ) {
@@ -233,4 +357,18 @@ private static String headerValueToString(Object o) {
233357
234358 return o .toString ();
235359 }
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+ }
236374}
0 commit comments