27
27
import com .google .bigtable .admin .v2 .ProjectName ;
28
28
import com .google .cloud .bigtable .admin .v2 .models .CreateInstanceRequest ;
29
29
import com .google .cloud .bigtable .admin .v2 .models .Instance ;
30
+ import com .google .cloud .bigtable .admin .v2 .models .PartialListInstancesException ;
30
31
import com .google .cloud .bigtable .admin .v2 .models .UpdateInstanceRequest ;
31
32
import com .google .cloud .bigtable .admin .v2 .stub .BigtableInstanceAdminStub ;
32
33
import com .google .common .base .Verify ;
33
34
import com .google .common .collect .ImmutableList ;
35
+ import com .google .common .util .concurrent .Futures ;
34
36
import com .google .common .util .concurrent .MoreExecutors ;
37
+ import com .google .common .util .concurrent .UncheckedExecutionException ;
35
38
import com .google .protobuf .Empty ;
36
39
import java .io .IOException ;
37
40
import java .util .List ;
@@ -96,7 +99,7 @@ public static BigtableInstanceAdminClient create(@Nonnull BigtableInstanceAdminS
96
99
return create (settings .getProjectName (), settings .getStubSettings ().createStub ());
97
100
}
98
101
99
- /** Constructs an instance of BigtableInstanceAdminClient with the given Projectname and stub. */
102
+ /** Constructs an instance of BigtableInstanceAdminClient with the given ProjectName and stub. */
100
103
public static BigtableInstanceAdminClient create (@ Nonnull ProjectName projectName ,
101
104
@ Nonnull BigtableInstanceAdminStub stub ) {
102
105
return new BigtableInstanceAdminClient (projectName , stub );
@@ -110,6 +113,7 @@ private BigtableInstanceAdminClient(
110
113
}
111
114
112
115
/** Gets the ProjectName this client is associated with. */
116
+ @ SuppressWarnings ("WeakerAccess" )
113
117
public ProjectName getProjectName () {
114
118
return projectName ;
115
119
}
@@ -125,6 +129,7 @@ public void close() {
125
129
*
126
130
* @see CreateInstanceRequest for details.
127
131
*/
132
+ @ SuppressWarnings ("WeakerAccess" )
128
133
public Instance createInstance (CreateInstanceRequest request ) {
129
134
return awaitFuture (createInstanceAsync (request ));
130
135
}
@@ -134,10 +139,16 @@ public Instance createInstance(CreateInstanceRequest request) {
134
139
*
135
140
* @see CreateInstanceRequest for details.
136
141
*/
142
+ @ SuppressWarnings ("WeakerAccess" )
137
143
public ApiFuture <Instance > createInstanceAsync (CreateInstanceRequest request ) {
138
144
return ApiFutures .transform (
139
145
stub .createInstanceOperationCallable ().futureCall (request .toProto (projectName )),
140
- Instance .PROTO_TRANSFORMER ,
146
+ new ApiFunction <com .google .bigtable .admin .v2 .Instance , Instance >() {
147
+ @ Override
148
+ public Instance apply (com .google .bigtable .admin .v2 .Instance proto ) {
149
+ return Instance .fromProto (proto );
150
+ }
151
+ },
141
152
MoreExecutors .directExecutor ());
142
153
}
143
154
@@ -146,6 +157,7 @@ public ApiFuture<Instance> createInstanceAsync(CreateInstanceRequest request) {
146
157
*
147
158
* @see UpdateInstanceRequest for details.
148
159
*/
160
+ @ SuppressWarnings ("WeakerAccess" )
149
161
public Instance updateInstance (UpdateInstanceRequest request ) {
150
162
return awaitFuture (updateInstanceAsync (request ));
151
163
}
@@ -155,21 +167,28 @@ public Instance updateInstance(UpdateInstanceRequest request) {
155
167
*
156
168
* @see UpdateInstanceRequest for details.
157
169
*/
170
+ @ SuppressWarnings ("WeakerAccess" )
158
171
public ApiFuture <Instance > updateInstanceAsync (UpdateInstanceRequest request ) {
159
172
return ApiFutures .transform (
160
173
stub .partialUpdateInstanceOperationCallable ().futureCall (request .toProto (projectName )),
161
- Instance .PROTO_TRANSFORMER ,
174
+ new ApiFunction <com .google .bigtable .admin .v2 .Instance , Instance >() {
175
+ @ Override
176
+ public Instance apply (com .google .bigtable .admin .v2 .Instance proto ) {
177
+ return Instance .fromProto (proto );
178
+ }
179
+ },
162
180
MoreExecutors .directExecutor ());
163
181
}
164
182
165
183
/** Get the instance representation. */
184
+ @ SuppressWarnings ("WeakerAccess" )
166
185
public Instance getInstance (String id ) {
167
186
return awaitFuture (getInstanceAsync (id ));
168
187
}
169
188
170
189
/** Asynchronously gets the instance representation wrapped in a future. */
190
+ @ SuppressWarnings ("WeakerAccess" )
171
191
public ApiFuture <Instance > getInstanceAsync (String instanceId ) {
172
-
173
192
InstanceName name = InstanceName .of (projectName .getProject (), instanceId );
174
193
175
194
GetInstanceRequest request = GetInstanceRequest .newBuilder ()
@@ -178,16 +197,23 @@ public ApiFuture<Instance> getInstanceAsync(String instanceId) {
178
197
179
198
return ApiFutures .transform (
180
199
stub .getInstanceCallable ().futureCall (request ),
181
- Instance .PROTO_TRANSFORMER ,
200
+ new ApiFunction <com .google .bigtable .admin .v2 .Instance , Instance >() {
201
+ @ Override
202
+ public Instance apply (com .google .bigtable .admin .v2 .Instance proto ) {
203
+ return Instance .fromProto (proto );
204
+ }
205
+ },
182
206
MoreExecutors .directExecutor ());
183
207
}
184
208
185
209
/** Lists all of the instances in the current project. */
210
+ @ SuppressWarnings ("WeakerAccess" )
186
211
public List <Instance > listInstances () {
187
212
return awaitFuture (listInstancesAsync ());
188
213
}
189
214
190
215
/** Asynchronously lists all of the instances in the current project. */
216
+ @ SuppressWarnings ("WeakerAccess" )
191
217
public ApiFuture <List <Instance >> listInstancesAsync () {
192
218
ListInstancesRequest request = ListInstancesRequest .newBuilder ()
193
219
.setParent (projectName .toString ())
@@ -196,41 +222,47 @@ public ApiFuture<List<Instance>> listInstancesAsync() {
196
222
ApiFuture <ListInstancesResponse > responseFuture = stub .listInstancesCallable ()
197
223
.futureCall (request );
198
224
199
- return ApiFutures .transform (responseFuture , new ApiFunction <ListInstancesResponse , List <Instance >>() {
200
- @ Override
201
- public List <Instance > apply (ListInstancesResponse proto ) {
202
- // NOTE: pagination is intentionally ignored. The server does not implement it.
203
- Verify .verify (proto .getNextPageToken ().isEmpty (),
204
- "Server returned an unexpected paginated response" );
205
-
206
- ImmutableList .Builder <Instance > instances = ImmutableList .builder ();
207
-
208
- for (com .google .bigtable .admin .v2 .Instance protoInstance : proto .getInstancesList ()) {
209
- instances .add (Instance .PROTO_TRANSFORMER .apply (protoInstance ));
210
- }
211
-
212
- ImmutableList .Builder <String > failedZones = ImmutableList .builder ();
213
- for (String locationStr : proto .getFailedLocationsList ()) {
214
- failedZones .add (LocationName .parse (locationStr ).getLocation ());
215
- }
216
-
217
-
218
- if (!failedZones .build ().isEmpty ()) {
219
- throw new PartialListInstancesException (failedZones .build (), instances .build ());
220
- }
221
-
222
- return instances .build ();
223
- }
224
- }, MoreExecutors .directExecutor ());
225
+ return ApiFutures
226
+ .transform (responseFuture , new ApiFunction <ListInstancesResponse , List <Instance >>() {
227
+ @ Override
228
+ public List <Instance > apply (ListInstancesResponse proto ) {
229
+ // NOTE: pagination is intentionally ignored. The server does not implement it.
230
+ Verify .verify (proto .getNextPageToken ().isEmpty (),
231
+ "Server returned an unexpected paginated response" );
232
+
233
+ ImmutableList .Builder <Instance > instances = ImmutableList .builder ();
234
+
235
+ for (com .google .bigtable .admin .v2 .Instance protoInstance : proto .getInstancesList ()) {
236
+ instances .add (Instance .fromProto (protoInstance ));
237
+ }
238
+
239
+ ImmutableList .Builder <String > failedZones = ImmutableList .builder ();
240
+ for (String locationStr : proto .getFailedLocationsList ()) {
241
+ LocationName fullLocation = LocationName .parse (locationStr );
242
+ if (fullLocation == null ) {
243
+ continue ;
244
+ }
245
+ failedZones .add (fullLocation .getLocation ());
246
+ }
247
+
248
+ if (!failedZones .build ().isEmpty ()) {
249
+ throw new PartialListInstancesException (failedZones .build (), instances .build ());
250
+ }
251
+
252
+ return instances .build ();
253
+ }
254
+ }, MoreExecutors .directExecutor ());
225
255
}
226
256
227
257
/** Deletes the specified instance. */
258
+ @ SuppressWarnings ("WeakerAccess" )
228
259
public void deleteInstance (String instanceId ) {
229
260
awaitFuture (deleteInstanceAsync (instanceId ));
230
261
}
231
262
232
263
/** Asynchronously deletes the specified instance. */
233
- private ApiFuture <Void > deleteInstanceAsync (String instanceId ) {
264
+ @ SuppressWarnings ("WeakerAccess" )
265
+ public ApiFuture <Void > deleteInstanceAsync (String instanceId ) {
234
266
InstanceName instanceName = InstanceName .of (projectName .getProject (), instanceId );
235
267
236
268
DeleteInstanceRequest request = DeleteInstanceRequest .newBuilder ()
@@ -248,39 +280,32 @@ public Void apply(Empty input) {
248
280
);
249
281
}
250
282
251
-
252
- private <T > T awaitFuture (ApiFuture <T > future ) {
253
- try {
254
- return future .get ();
255
- } catch (Throwable t ) {
256
- // TODO(igorbernstein2): figure out a better wrapper exception.
257
- throw new RuntimeException (t );
258
- }
259
- }
260
-
261
283
/**
262
- * Exception thrown when some zones are unavailable and listInstances is unable to return a full
263
- * instance list. This exception can be inspected to get a partial list.
284
+ * Awaits the result of a future, taking care to propagate errors while maintaining the call site
285
+ * in a suppressed exception. This allows semantic errors to be caught across threads, while
286
+ * preserving the call site in the error. The caller's stacktrace will be made available as a
287
+ * suppressed exception.
264
288
*/
265
- public static class PartialListInstancesException extends RuntimeException {
266
- private final List <String > failedZones ;
267
- private final List <Instance > instances ;
268
-
269
- PartialListInstancesException (List <String > failedZones , List <Instance > instances ) {
270
- super ("Failed to list all instances, some zones where unavailable" );
289
+ // TODO(igorbernstein2): try to move this into gax
290
+ private <T > T awaitFuture (ApiFuture <T > future ) {
291
+ RuntimeException error ;
271
292
272
- this .failedZones = failedZones ;
273
- this .instances = instances ;
293
+ try {
294
+ return Futures .getUnchecked (future );
295
+ } catch (UncheckedExecutionException e ) {
296
+ if (e .getCause () instanceof RuntimeException ) {
297
+ error = (RuntimeException ) e .getCause ();
298
+ } else {
299
+ error = e ;
300
+ }
301
+ } catch (RuntimeException e ) {
302
+ error = e ;
274
303
}
275
304
276
- /** A list of zones, whose unavailability caused this error. */
277
- public List <String > getFailedZones () {
278
- return failedZones ;
279
- }
305
+ // Add the caller's stack as a suppressed exception
306
+ error .addSuppressed (new RuntimeException ("Encountered error while awaiting future" ));
280
307
281
- /** A partial list of instances that were found in the available zones. */
282
- public List <Instance > getInstances () {
283
- return instances ;
284
- }
308
+ throw error ;
285
309
}
310
+
286
311
}
0 commit comments