1616
1717package com .google .cloud ;
1818
19+ import static com .google .common .base .MoreObjects .firstNonNull ;
20+
1921import com .google .cloud .spi .ServiceRpcFactory ;
22+ import com .google .common .annotations .VisibleForTesting ;
2023import com .google .common .base .Preconditions ;
2124
2225import io .grpc .internal .SharedResourceHolder ;
2326import io .grpc .internal .SharedResourceHolder .Resource ;
2427
28+ import java .io .IOException ;
29+ import java .io .ObjectInputStream ;
2530import java .util .Objects ;
2631import java .util .concurrent .ScheduledExecutorService ;
2732import java .util .concurrent .ScheduledThreadPoolExecutor ;
@@ -40,10 +45,13 @@ public abstract class GrpcServiceOptions<ServiceT extends Service<OptionsT>, Ser
4045 extends ServiceOptions <ServiceT , ServiceRpcT , OptionsT > {
4146
4247 private static final long serialVersionUID = 6415982522610509549L ;
48+ private final String executorFactoryClassName ;
4349 private final int initialTimeout ;
4450 private final double timeoutMultiplier ;
4551 private final int maxTimeout ;
4652
53+ private transient ExecutorFactory executorFactory ;
54+
4755 /**
4856 * Shared thread pool executor.
4957 */
@@ -65,45 +73,41 @@ public void close(ScheduledExecutorService instance) {
6573 };
6674
6775 /**
68- * An interface that provides access to a scheduled executor service.
76+ * An interface for {@link ScheduledExecutorService} factories. Implementations of this interface
77+ * can be used to provide an user-defined scheduled executor to execute requests. Any
78+ * implementation of this interface must override the {@code get()} method to return the desired
79+ * executor. The {@code release(executor)} method should be overriden to free resources used by
80+ * the executor (if needed) according to application's logic.
81+ *
82+ * <p>Implementation must provide a public no-arg constructor. Loading of a factory implementation
83+ * is done via {@link java.util.ServiceLoader}.
6984 */
70- public interface ExecutorProvider {
85+ public interface ExecutorFactory {
7186
7287 /**
73- * Returns the scheduled executor service.
88+ * Gets a scheduled executor service instance .
7489 */
7590 ScheduledExecutorService get ();
7691
7792 /**
78- * Shuts down the scheduled executor service if it is no longer used .
93+ * Releases resources used by the executor and possibly shuts it down .
7994 */
80- void shutdown ( );
95+ void release ( ScheduledExecutorService executor );
8196 }
8297
83- /**
84- * An interface that provides access to a scheduled executor service.
85- */
86- private static class DefaultExecutorProvider implements ExecutorProvider {
87-
88- private ScheduledExecutorService service ;
89- private boolean shutdown = false ;
98+ @ VisibleForTesting
99+ static class DefaultExecutorFactory implements ExecutorFactory {
90100
91- private DefaultExecutorProvider () {}
101+ private static final DefaultExecutorFactory INSTANCE = new DefaultExecutorFactory ();
92102
93103 @ Override
94- public synchronized ScheduledExecutorService get () {
95- if (service == null && !shutdown ) {
96- service = SharedResourceHolder .get (EXECUTOR );
97- }
98- return service ;
104+ public ScheduledExecutorService get () {
105+ return SharedResourceHolder .get (EXECUTOR );
99106 }
100107
101108 @ Override
102- public synchronized void shutdown () {
103- if (service != null && !shutdown ) {
104- shutdown = true ;
105- service = SharedResourceHolder .release (EXECUTOR , service );
106- }
109+ public synchronized void release (ScheduledExecutorService executor ) {
110+ SharedResourceHolder .release (EXECUTOR , executor );
107111 }
108112 }
109113
@@ -120,6 +124,7 @@ protected abstract static class Builder<ServiceT extends Service<OptionsT>, Serv
120124 B extends Builder <ServiceT , ServiceRpcT , OptionsT , B >>
121125 extends ServiceOptions .Builder <ServiceT , ServiceRpcT , OptionsT , B > {
122126
127+ private ExecutorFactory executorFactory ;
123128 private int initialTimeout = 20_000 ;
124129 private double timeoutMultiplier = 1.5 ;
125130 private int maxTimeout = 100_000 ;
@@ -128,6 +133,7 @@ protected Builder() {}
128133
129134 protected Builder (GrpcServiceOptions <ServiceT , ServiceRpcT , OptionsT > options ) {
130135 super (options );
136+ executorFactory = options .executorFactory ;
131137 initialTimeout = options .initialTimeout ;
132138 timeoutMultiplier = options .timeoutMultiplier ;
133139 maxTimeout = options .maxTimeout ;
@@ -136,6 +142,17 @@ protected Builder(GrpcServiceOptions<ServiceT, ServiceRpcT, OptionsT> options) {
136142 @ Override
137143 protected abstract GrpcServiceOptions <ServiceT , ServiceRpcT , OptionsT > build ();
138144
145+ /**
146+ * Sets the scheduled executor factory. This method can be used to provide an user-defined
147+ * scheduled executor to execute requests.
148+ *
149+ * @return the builder
150+ */
151+ public B executorFactory (ExecutorFactory executorFactory ) {
152+ this .executorFactory = executorFactory ;
153+ return self ();
154+ }
155+
139156 /**
140157 * Sets the timeout for the initial RPC, in milliseconds. Subsequent calls will use this value
141158 * adjusted according to {@link #timeoutMultiplier(double)}. Default value is 20000.
@@ -180,6 +197,9 @@ protected GrpcServiceOptions(
180197 Class <? extends ServiceRpcFactory <ServiceRpcT , OptionsT >> rpcFactoryClass , Builder <ServiceT ,
181198 ServiceRpcT , OptionsT , ?> builder ) {
182199 super (serviceFactoryClass , rpcFactoryClass , builder );
200+ executorFactory = firstNonNull (builder .executorFactory ,
201+ getFromServiceLoader (ExecutorFactory .class , DefaultExecutorFactory .INSTANCE ));
202+ executorFactoryClassName = executorFactory .getClass ().getName ();
183203 initialTimeout = builder .initialTimeout ;
184204 timeoutMultiplier = builder .timeoutMultiplier ;
185205 maxTimeout = builder .maxTimeout <= initialTimeout ? initialTimeout : builder .maxTimeout ;
@@ -188,8 +208,8 @@ protected GrpcServiceOptions(
188208 /**
189209 * Returns a scheduled executor service provider.
190210 */
191- protected ExecutorProvider executorProvider () {
192- return new DefaultExecutorProvider () ;
211+ protected ExecutorFactory executorFactory () {
212+ return executorFactory ;
193213 }
194214
195215 /**
@@ -217,13 +237,20 @@ public int maxTimeout() {
217237
218238 @ Override
219239 protected int baseHashCode () {
220- return Objects .hash (super .baseHashCode (), initialTimeout , timeoutMultiplier , maxTimeout );
240+ return Objects .hash (super .baseHashCode (), executorFactoryClassName , initialTimeout ,
241+ timeoutMultiplier , maxTimeout );
221242 }
222243
223244 protected boolean baseEquals (GrpcServiceOptions <?, ?, ?> other ) {
224245 return super .baseEquals (other )
246+ && Objects .equals (executorFactoryClassName , other .executorFactoryClassName )
225247 && Objects .equals (initialTimeout , other .initialTimeout )
226248 && Objects .equals (timeoutMultiplier , other .timeoutMultiplier )
227249 && Objects .equals (maxTimeout , other .maxTimeout );
228250 }
251+
252+ private void readObject (ObjectInputStream input ) throws IOException , ClassNotFoundException {
253+ input .defaultReadObject ();
254+ executorFactory = newInstance (executorFactoryClassName );
255+ }
229256}
0 commit comments