Skip to content

Commit 342a7cd

Browse files
authored
Transform exceptions on server streaming calls. (#349)
1 parent 7618fdb commit 342a7cd

10 files changed

Lines changed: 588 additions & 105 deletions

google-ads/src/main/java/com/google/ads/googleads/lib/GrpcGoogleAdsCallableFactory.java

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616
package com.google.ads.googleads.lib;
1717

18-
import com.google.ads.googleads.lib.ExceptionTransformingCallable;
19-
import com.google.ads.googleads.lib.ExceptionTransformingCallable.ExceptionTransformation;
20-
import com.google.ads.googleads.lib.GoogleAdsExceptionTransformation;
18+
import com.google.ads.googleads.lib.callables.ExceptionTransformation;
19+
import com.google.ads.googleads.lib.callables.ExceptionTransformingServerStreamingCallable;
20+
import com.google.ads.googleads.lib.callables.ExceptionTransformingUnaryCallable;
21+
import com.google.ads.googleads.lib.callables.GoogleAdsExceptionTransformation;
2122
import com.google.api.gax.grpc.GrpcCallSettings;
2223
import com.google.api.gax.grpc.GrpcCallableFactory;
2324
import com.google.api.gax.grpc.GrpcStubCallableFactory;
@@ -37,42 +38,61 @@
3738
import com.google.longrunning.Operation;
3839
import com.google.longrunning.stub.OperationsStub;
3940

41+
/**
42+
* Defines the factory used to create instances for all Google Ads services.
43+
*
44+
* <p>Used in place of the default generated code to override the exceptions generated to throw
45+
* GoogleAdsException instead of ApiException.
46+
*/
4047
public class GrpcGoogleAdsCallableFactory implements GrpcStubCallableFactory {
4148

42-
private static final ExceptionTransformation googleAdsExceptionTransformation = new GoogleAdsExceptionTransformation();
49+
private static final ExceptionTransformation googleAdsExceptionTransformation =
50+
new GoogleAdsExceptionTransformation();
4351

44-
public static <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createBaseUnaryCallable(GrpcCallSettings<RequestT, ResponseT> grpcCallSettings, UnaryCallSettings<?, ?> callSettings, ClientContext clientContext) {
45-
UnaryCallable<RequestT, ResponseT> callable = GrpcCallableFactory.createBaseUnaryCallable(grpcCallSettings, callSettings, clientContext);
46-
return new ExceptionTransformingCallable<>(callable, googleAdsExceptionTransformation);
52+
public static <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createBaseUnaryCallable(
53+
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
54+
UnaryCallSettings<?, ?> callSettings,
55+
ClientContext clientContext) {
56+
UnaryCallable<RequestT, ResponseT> callable =
57+
GrpcCallableFactory.createBaseUnaryCallable(grpcCallSettings, callSettings, clientContext);
58+
return new ExceptionTransformingUnaryCallable<>(callable, googleAdsExceptionTransformation);
4759
}
4860

61+
@Override
4962
public <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createUnaryCallable(
5063
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
5164
UnaryCallSettings<RequestT, ResponseT> callSettings,
5265
ClientContext clientContext) {
53-
UnaryCallable<RequestT, ResponseT> callable = createBaseUnaryCallable(grpcCallSettings, callSettings, clientContext);
66+
UnaryCallable<RequestT, ResponseT> callable =
67+
createBaseUnaryCallable(grpcCallSettings, callSettings, clientContext);
5468
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
5569
}
5670

71+
@Override
5772
public <RequestT, ResponseT, PagedListResponseT>
5873
UnaryCallable<RequestT, PagedListResponseT> createPagedCallable(
5974
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
6075
PagedCallSettings<RequestT, ResponseT, PagedListResponseT> pagedCallSettings,
6176
ClientContext clientContext) {
62-
UnaryCallable<RequestT, ResponseT> innerCallable = createBaseUnaryCallable(grpcCallSettings, pagedCallSettings, clientContext);
63-
UnaryCallable<RequestT, PagedListResponseT> pagedCallable = Callables.paged(innerCallable, pagedCallSettings);
77+
UnaryCallable<RequestT, ResponseT> innerCallable =
78+
createBaseUnaryCallable(grpcCallSettings, pagedCallSettings, clientContext);
79+
UnaryCallable<RequestT, PagedListResponseT> pagedCallable =
80+
Callables.paged(innerCallable, pagedCallSettings);
6481
return pagedCallable.withDefaultCallContext(clientContext.getDefaultCallContext());
6582
}
6683

84+
@Override
6785
public <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createBatchingCallable(
6886
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
6987
BatchingCallSettings<RequestT, ResponseT> batchingCallSettings,
7088
ClientContext clientContext) {
71-
UnaryCallable<RequestT, ResponseT> callable = createBaseUnaryCallable(grpcCallSettings, batchingCallSettings, clientContext);
89+
UnaryCallable<RequestT, ResponseT> callable =
90+
createBaseUnaryCallable(grpcCallSettings, batchingCallSettings, clientContext);
7291
callable = Callables.batching(callable, batchingCallSettings, clientContext);
7392
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
7493
}
7594

95+
@Override
7696
public <RequestT, ResponseT, MetadataT>
7797
OperationCallable<RequestT, ResponseT, MetadataT> createOperationCallable(
7898
GrpcCallSettings<RequestT, Operation> grpcCallSettings,
@@ -83,6 +103,7 @@ OperationCallable<RequestT, ResponseT, MetadataT> createOperationCallable(
83103
grpcCallSettings, operationCallSettings, clientContext, operationsStub);
84104
}
85105

106+
@Override
86107
public <RequestT, ResponseT>
87108
BidiStreamingCallable<RequestT, ResponseT> createBidiStreamingCallable(
88109
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
@@ -92,15 +113,20 @@ BidiStreamingCallable<RequestT, ResponseT> createBidiStreamingCallable(
92113
grpcCallSettings, streamingCallSettings, clientContext);
93114
}
94115

116+
@Override
95117
public <RequestT, ResponseT>
96118
ServerStreamingCallable<RequestT, ResponseT> createServerStreamingCallable(
97119
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
98120
ServerStreamingCallSettings<RequestT, ResponseT> streamingCallSettings,
99121
ClientContext clientContext) {
100-
return GrpcCallableFactory.createServerStreamingCallable(
101-
grpcCallSettings, streamingCallSettings, clientContext);
122+
ServerStreamingCallable<RequestT, ResponseT> defaultCallable =
123+
GrpcCallableFactory.createServerStreamingCallable(
124+
grpcCallSettings, streamingCallSettings, clientContext);
125+
return new ExceptionTransformingServerStreamingCallable(
126+
defaultCallable, new GoogleAdsExceptionTransformation());
102127
}
103128

129+
@Override
104130
public <RequestT, ResponseT>
105131
ClientStreamingCallable<RequestT, ResponseT> createClientStreamingCallable(
106132
GrpcCallSettings<RequestT, ResponseT> grpcCallSettings,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.ads.googleads.lib.callables;
16+
17+
/** Represents a transformation between {@link Throwable}s. */
18+
public interface ExceptionTransformation {
19+
20+
/**
21+
* Transforms an input throwable to an output throwable.
22+
*
23+
* <p>If no transformation is applied this must return the input throwable.
24+
*/
25+
Throwable transform(Throwable throwable);
26+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.ads.googleads.lib.callables;
16+
17+
import com.google.api.gax.rpc.ApiCallContext;
18+
import com.google.api.gax.rpc.ApiException;
19+
import com.google.api.gax.rpc.ResponseObserver;
20+
import com.google.api.gax.rpc.ServerStreamingCallable;
21+
import com.google.api.gax.rpc.StreamController;
22+
23+
/**
24+
* Wrapper around a {@link ServerStreamingCallable} which invokes an {@link ExceptionTransformation}
25+
* for {@link Throwable}s which occur on the stream.
26+
*
27+
* <p>NOTE: This class could be pushed into the gax library, as it is not specific to the Google Ads
28+
* API.
29+
*/
30+
public class ExceptionTransformingServerStreamingCallable<RequestT, ResponseT>
31+
extends ServerStreamingCallable<RequestT, ResponseT> {
32+
33+
private final ServerStreamingCallable<RequestT, ResponseT> innerCallable;
34+
private final ExceptionTransformation exceptionTransformation;
35+
36+
public ExceptionTransformingServerStreamingCallable(
37+
ServerStreamingCallable<RequestT, ResponseT> innerCallable,
38+
ExceptionTransformation exceptionTransformation) {
39+
this.innerCallable = innerCallable;
40+
this.exceptionTransformation = exceptionTransformation;
41+
}
42+
43+
@Override
44+
public void call(
45+
RequestT request, ResponseObserver<ResponseT> responseObserver, ApiCallContext context) {
46+
innerCallable.call(request, new ExceptionTransformingStreamObserver(responseObserver), context);
47+
}
48+
49+
/** Provides a mechanism for transforming any exceptions which occur on the stream. */
50+
private class ExceptionTransformingStreamObserver implements ResponseObserver<ResponseT> {
51+
52+
private final ResponseObserver<ResponseT> innerObserver;
53+
54+
public ExceptionTransformingStreamObserver(ResponseObserver<ResponseT> innerObserver) {
55+
this.innerObserver = innerObserver;
56+
}
57+
58+
@Override
59+
public void onStart(StreamController controller) {
60+
innerObserver.onStart(controller);
61+
}
62+
63+
@Override
64+
public void onResponse(ResponseT response) {
65+
innerObserver.onResponse(response);
66+
}
67+
68+
@Override
69+
public void onError(Throwable t) {
70+
if (t instanceof ApiException) {
71+
t = exceptionTransformation.transform(t);
72+
}
73+
innerObserver.onError(t);
74+
}
75+
76+
@Override
77+
public void onComplete() {
78+
innerObserver.onComplete();
79+
}
80+
}
81+
}

google-ads/src/main/java/com/google/ads/googleads/lib/ExceptionTransformingCallable.java renamed to google-ads/src/main/java/com/google/ads/googleads/lib/callables/ExceptionTransformingUnaryCallable.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,32 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package com.google.ads.googleads.lib;
15+
package com.google.ads.googleads.lib.callables;
1616

1717
import com.google.api.core.AbstractApiFuture;
1818
import com.google.api.core.ApiFuture;
1919
import com.google.api.core.ApiFutureCallback;
2020
import com.google.api.core.ApiFutures;
2121
import com.google.api.gax.grpc.GrpcCallContext;
2222
import com.google.api.gax.rpc.ApiCallContext;
23-
import com.google.api.gax.rpc.ApiException;
2423
import com.google.api.gax.rpc.UnaryCallable;
2524
import com.google.common.base.Preconditions;
2625
import java.util.concurrent.CancellationException;
2726

2827
/**
29-
* NOTE: This class could be pushed into the gax library, as it is not specific to the Google Ads
28+
* Wrapper around a {@link UnaryCallable} which invokes an {@link ExceptionTransformation} for
29+
* {@link Throwable}s which occur on the stream.
30+
*
31+
* <p>NOTE: This class could be pushed into the gax library, as it is not specific to the Google Ads
3032
* API.
3133
*/
32-
public class ExceptionTransformingCallable<RequestT, ResponseT>
34+
public class ExceptionTransformingUnaryCallable<RequestT, ResponseT>
3335
extends UnaryCallable<RequestT, ResponseT> {
3436

35-
public interface ExceptionTransformation {
36-
Throwable transform(ApiException throwable);
37-
}
38-
3937
private final UnaryCallable<RequestT, ResponseT> callable;
4038
private final ExceptionTransformation transformation;
4139

42-
public ExceptionTransformingCallable(
40+
public ExceptionTransformingUnaryCallable(
4341
UnaryCallable<RequestT, ResponseT> callable, ExceptionTransformation transformation) {
4442
this.callable = Preconditions.checkNotNull(callable);
4543
this.transformation = transformation;
@@ -55,6 +53,7 @@ public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputCon
5553
return transformingFuture;
5654
}
5755

56+
/** Provides a mechanism to transform exceptions which occur on the inner callable. */
5857
private class ExceptionTransformingFuture extends AbstractApiFuture<ResponseT>
5958
implements ApiFutureCallback<ResponseT> {
6059
private ApiFuture<ResponseT> innerCallFuture;
@@ -79,8 +78,8 @@ public void onSuccess(ResponseT r) {
7978
public void onFailure(Throwable throwable) {
8079
if (throwable instanceof CancellationException && cancelled) {
8180
// this just circled around, so ignore.
82-
} else if (throwable instanceof ApiException) {
83-
setException(transformation.transform((ApiException) throwable));
81+
} else {
82+
setException(transformation.transform(throwable));
8483
}
8584
}
8685
}

google-ads/src/main/java/com/google/ads/googleads/lib/GoogleAdsExceptionTransformation.java renamed to google-ads/src/main/java/com/google/ads/googleads/lib/callables/GoogleAdsExceptionTransformation.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package com.google.ads.googleads.lib;
15+
package com.google.ads.googleads.lib.callables;
1616

17+
import com.google.ads.googleads.lib.BaseGoogleAdsException;
1718
import com.google.ads.googleads.lib.catalog.GeneratedCatalog;
1819
import com.google.ads.googleads.lib.catalog.Version;
1920
import com.google.api.gax.rpc.ApiException;
@@ -23,20 +24,21 @@
2324
* Transforms an ApiException into a GoogleAdsException whenever a binary GoogleAdsFailure message
2425
* was sent in the RPC trailers.
2526
*/
26-
public class GoogleAdsExceptionTransformation
27-
implements ExceptionTransformingCallable.ExceptionTransformation {
27+
public class GoogleAdsExceptionTransformation implements ExceptionTransformation {
2828

2929
private static final GeneratedCatalog catalog = GeneratedCatalog.getDefault();
3030

3131
@Override
32-
public Throwable transform(ApiException apiException) {
33-
for (Version version : catalog.getSupportedVersions()) {
34-
Optional<? extends BaseGoogleAdsException> result =
35-
version.getExceptionFactory().createGoogleAdsException(apiException);
36-
if (result.isPresent()) {
37-
return result.get();
32+
public Throwable transform(Throwable input) {
33+
if (ApiException.class.isAssignableFrom(input.getClass())) {
34+
for (Version version : catalog.getSupportedVersions()) {
35+
Optional<? extends BaseGoogleAdsException> result =
36+
version.getExceptionFactory().createGoogleAdsException((ApiException) input);
37+
if (result.isPresent()) {
38+
return result.get();
39+
}
3840
}
3941
}
40-
return apiException;
42+
return input;
4143
}
4244
}

0 commit comments

Comments
 (0)