@@ -504,6 +504,11 @@ public class Options {
504504 * {@link Builder#executor(ExecutorService) executor}.
505505 */
506506 public static final String PROP_EXECUTOR_SERVICE_CLASS = "executor.service.class" ;
507+ /**
508+ * Property used to set class name for the Executor Service (executor) class
509+ * {@link Builder#executor(ExecutorService) executor}.
510+ */
511+ public static final String PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS = "scheduled.executor.service.class" ;
507512 /**
508513 * Property used to set class name for the Connect Thread Factory
509514 * {@link Builder#connectThreadFactory(ThreadFactory) connectThreadFactory}.
@@ -660,14 +665,15 @@ public class Options {
660665 private final ErrorListener errorListener ;
661666 private final TimeTraceLogger timeTraceLogger ;
662667 private final ConnectionListener connectionListener ;
663- private ReadListener readListener ;
668+ private final ReadListener readListener ;
664669 private final StatisticsCollector statisticsCollector ;
665670 private final String dataPortType ;
666671
667672 private final boolean trackAdvancedStats ;
668673 private final boolean traceConnection ;
669674
670675 private final ExecutorService executor ;
676+ private final ScheduledExecutorService scheduledExecutor ;
671677 private final ThreadFactory connectThreadFactory ;
672678 private final ThreadFactory callbackThreadFactory ;
673679 private final ServerPool serverPool ;
@@ -808,6 +814,7 @@ public static class Builder {
808814 private StatisticsCollector statisticsCollector = null ;
809815 private String dataPortType = DEFAULT_DATA_PORT_TYPE ;
810816 private ExecutorService executor ;
817+ private ScheduledExecutorService scheduledExecutor ;
811818 private ThreadFactory connectThreadFactory ;
812819 private ThreadFactory callbackThreadFactory ;
813820 private List <java .util .function .Consumer <HttpRequest >> httpRequestInterceptors ;
@@ -939,6 +946,7 @@ public Builder properties(Properties props) {
939946 classnameProperty (props , PROP_SERVERS_POOL_IMPLEMENTATION_CLASS , o -> this .serverPool = (ServerPool ) o );
940947 classnameProperty (props , PROP_DISPATCHER_FACTORY_CLASS , o -> this .dispatcherFactory = (DispatcherFactory ) o );
941948 classnameProperty (props , PROP_EXECUTOR_SERVICE_CLASS , o -> this .executor = (ExecutorService ) o );
949+ classnameProperty (props , PROP_SCHEDULED_EXECUTOR_SERVICE_CLASS , o -> this .scheduledExecutor = (ScheduledExecutorService ) o );
942950 classnameProperty (props , PROP_CONNECT_THREAD_FACTORY_CLASS , o -> this .connectThreadFactory = (ThreadFactory ) o );
943951 classnameProperty (props , PROP_CALLBACK_THREAD_FACTORY_CLASS , o -> this .callbackThreadFactory = (ThreadFactory ) o );
944952 return this ;
@@ -1630,6 +1638,19 @@ public Builder executor(ExecutorService executor) {
16301638 return this ;
16311639 }
16321640
1641+ /**
1642+ * Set the {@link ScheduledExecutorService ScheduledExecutorService} used to run scheduled task like
1643+ * heartbeat timers
1644+ * The default is a ScheduledThreadPoolExecutor that does not
1645+ * execute delayed tasks after shutdown and removes tasks on cancel;
1646+ * @param scheduledExecutor The ScheduledExecutorService to use for timer tasks
1647+ * @return the Builder for chaining
1648+ */
1649+ public Builder scheduledExecutor (ScheduledExecutorService scheduledExecutor ) {
1650+ this .scheduledExecutor = scheduledExecutor ;
1651+ return this ;
1652+ }
1653+
16331654 /**
16341655 * Sets custom thread factory for the executor service
16351656 *
@@ -1910,6 +1931,17 @@ else if (useDefaultTls) {
19101931 new DefaultThreadFactory (threadPrefix ));
19111932 }
19121933
1934+ if (this .scheduledExecutor == null ) {
1935+ String threadPrefix = nullOrEmpty (this .connectionName ) ? DEFAULT_THREAD_NAME_PREFIX : this .connectionName ;
1936+ // the core pool size of 3 is chosen considering where we know the scheduler is used.
1937+ // 1. Ping timer, 2. cleanup timer, 3. SocketDataPortWithWriteTimeout
1938+ // Pull message managers also use a scheduler, but we don't even know if this will be consuming
1939+ ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor (3 , new DefaultThreadFactory (threadPrefix ));
1940+ stpe .setExecuteExistingDelayedTasksAfterShutdownPolicy (false );
1941+ stpe .setRemoveOnCancelPolicy (true );
1942+ this .scheduledExecutor = stpe ;
1943+ }
1944+
19131945 if (socketReadTimeoutMillis > 0 ) {
19141946 long srtMin = pingInterval .toMillis () + MINIMUM_SOCKET_WRITE_TIMEOUT_GT_CONNECTION_TIMEOUT ;
19151947 if (socketReadTimeoutMillis < srtMin ) {
@@ -2014,6 +2046,7 @@ public Builder(Options o) {
20142046 this .dataPortType = o .dataPortType ;
20152047 this .trackAdvancedStats = o .trackAdvancedStats ;
20162048 this .executor = o .executor ;
2049+ this .scheduledExecutor = o .scheduledExecutor ;
20172050 this .callbackThreadFactory = o .callbackThreadFactory ;
20182051 this .connectThreadFactory = o .connectThreadFactory ;
20192052 this .httpRequestInterceptors = o .httpRequestInterceptors ;
@@ -2082,6 +2115,7 @@ private Options(Builder b) {
20822115 this .dataPortType = b .dataPortType ;
20832116 this .trackAdvancedStats = b .trackAdvancedStats ;
20842117 this .executor = b .executor ;
2118+ this .scheduledExecutor = b .scheduledExecutor ;
20852119 this .callbackThreadFactory = b .callbackThreadFactory ;
20862120 this .connectThreadFactory = b .connectThreadFactory ;
20872121 this .httpRequestInterceptors = b .httpRequestInterceptors ;
@@ -2107,6 +2141,13 @@ public ExecutorService getExecutor() {
21072141 return this .executor ;
21082142 }
21092143
2144+ /**
2145+ * @return the ScheduledExecutorService, see {@link Builder#scheduledExecutor(ScheduledExecutorService) scheduledExecutor()} in the builder doc
2146+ */
2147+ public ScheduledExecutorService getScheduledExecutor () {
2148+ return scheduledExecutor ;
2149+ }
2150+
21102151 /**
21112152 * @return the callback executor, see {@link Builder#callbackThreadFactory(ThreadFactory) callbackThreadFactory()} in the builder doc
21122153 */
0 commit comments