36
36
import io .grpc .ServerBuilder ;
37
37
import io .grpc .ServerServiceDefinition ;
38
38
import io .grpc .inprocess .InProcessServerBuilder ;
39
+ import io .spine .annotation .Experimental ;
39
40
import io .spine .client .ConnectionConstants ;
40
41
import org .checkerframework .checker .nullness .qual .Nullable ;
41
42
42
43
import java .io .IOException ;
43
44
import java .util .Optional ;
44
45
import java .util .Set ;
45
46
import java .util .concurrent .Executor ;
47
+ import java .util .function .Function ;
46
48
47
49
import static com .google .common .base .Preconditions .checkNotNull ;
48
50
import static com .google .common .base .Preconditions .checkState ;
51
+ import static io .spine .server .GrpcContainer .ConfigureServer .doNothing ;
49
52
import static java .lang .String .format ;
50
53
import static java .util .Objects .requireNonNull ;
51
54
@@ -70,6 +73,7 @@ public final class GrpcContainer {
70
73
private final @ Nullable Integer port ;
71
74
private final @ Nullable String serverName ;
72
75
private final ImmutableSet <ServerServiceDefinition > services ;
76
+ private final ConfigureServer configureServer ;
73
77
74
78
private @ Nullable Server grpcServer ;
75
79
@@ -106,6 +110,9 @@ private GrpcContainer(Builder builder) {
106
110
this .port = builder .port ().orElse (null );
107
111
this .serverName = builder .serverName ().orElse (null );
108
112
this .services = builder .services ();
113
+ this .configureServer = builder .configureServer == null
114
+ ? doNothing ()
115
+ : builder .configureServer ;
109
116
}
110
117
111
118
/**
@@ -284,6 +291,7 @@ private Server createGrpcServer(@Nullable Executor executor) {
284
291
for (ServerServiceDefinition service : services ) {
285
292
builder .addService (service );
286
293
}
294
+ builder = configureServer .apply (builder );
287
295
return builder .build ();
288
296
}
289
297
@@ -324,7 +332,8 @@ private static ServerBuilder<?> builderAtPort(Integer port, @Nullable Executor e
324
332
* Injects a server to this container.
325
333
*
326
334
* <p>All calls to {@link #createGrpcServer(Executor)} will resolve to the given server
327
- * instance.
335
+ * instance. The server instance is used as-is, no other
336
+ * {@linkplain Builder#withServer(ConfigureServer) configuration methods} have any effect on it.
328
337
*
329
338
* <p>A test-only method.
330
339
*/
@@ -380,6 +389,7 @@ private void println(@FormatString String msgFormat, Object... arg) {
380
389
public static final class Builder extends ConnectionBuilder {
381
390
382
391
private final Set <ServerServiceDefinition > services = Sets .newHashSet ();
392
+ private @ Nullable ConfigureServer configureServer ;
383
393
384
394
private Builder (@ Nullable Integer port , @ Nullable String serverName ) {
385
395
super (port , serverName );
@@ -405,18 +415,52 @@ public int getPort() {
405
415
return port ().orElse (0 );
406
416
}
407
417
418
+ /**
419
+ * Adds a gRPC service to deploy within the container being built.
420
+ *
421
+ * @return this instance of {@code Builder}, for call chaining
422
+ */
408
423
@ CanIgnoreReturnValue
409
424
public Builder addService (BindableService service ) {
425
+ checkNotNull (service );
410
426
services .add (service .bindService ());
411
427
return this ;
412
428
}
413
429
430
+ /**
431
+ * Removes the {@linkplain #addService(BindableService) previously added}
432
+ * gRPC service.
433
+ *
434
+ * <p>If the service under the given definition was not added previously,
435
+ * this method does nothing.
436
+ *
437
+ * @return this instance of {@code Builder}, for call chaining
438
+ */
414
439
@ CanIgnoreReturnValue
415
440
public Builder removeService (ServerServiceDefinition service ) {
416
441
services .remove (service );
417
442
return this ;
418
443
}
419
444
445
+ /**
446
+ * Sets an additional configuration action for the gRPC {@link Server} instance,
447
+ * created for this {@code GrpcContainer} to run on top of. This configuration is applied
448
+ * right before the {@linkplain #start() server is started}.
449
+ *
450
+ * <p>Allows the direct access to gRPC {@link ServerBuilder}'s API.
451
+ *
452
+ * <p>Please note this API is experimental.
453
+ *
454
+ * @return this instance of {@code Builder}, for call chaining
455
+ * @see ConfigureServer
456
+ */
457
+ @ Experimental
458
+ @ CanIgnoreReturnValue
459
+ public Builder withServer (ConfigureServer action ) {
460
+ this .configureServer = checkNotNull (action );
461
+ return this ;
462
+ }
463
+
420
464
/**
421
465
* Obtains the services already added to the builder.
422
466
*
@@ -438,4 +482,40 @@ public GrpcContainer build() {
438
482
return new GrpcContainer (this );
439
483
}
440
484
}
485
+
486
+ /**
487
+ * Allows to configure the gRPC's {@link Server} instance,
488
+ * on top of which this {@code GrpcContainer} will operate.
489
+ *
490
+ * <p>It is expected that the obtained builder of gRPC server is used to perform
491
+ * some fine-grained tuning of its features. The same instance of {@link ServerBuilder}
492
+ * should be returned.
493
+ *
494
+ * <p>Example.
495
+ *
496
+ * <pre>
497
+ *
498
+ * GrpcContainer container =
499
+ * GrpcContainer.atPort(1654)
500
+ * {@literal .withServer((server) -> server.maxInboundMessageSize(16_000_000)) }
501
+ * // ...
502
+ * .build();
503
+ *
504
+ * </pre>
505
+ *
506
+ * <p>Please note this interface is a part of experimental API.
507
+ *
508
+ * @see Builder#withServer(ConfigureServer)
509
+ */
510
+ @ Experimental
511
+ @ FunctionalInterface
512
+ public interface ConfigureServer extends Function <ServerBuilder <?>, ServerBuilder <?>> {
513
+
514
+ /**
515
+ * Returns an instance which does nothing and returns the same {@code ServerBuilder}.
516
+ */
517
+ static ConfigureServer doNothing () {
518
+ return builder -> builder ;
519
+ }
520
+ }
441
521
}
0 commit comments