2424import java .io .IOException ;
2525import java .util .Objects ;
2626import java .util .concurrent .Executors ;
27- import java .util .concurrent .Future ;
2827import java .util .concurrent .ScheduledExecutorService ;
2928import java .util .concurrent .TimeUnit ;
30- import java .util .concurrent .atomic .AtomicReference ;
3129import org .apache .curator .framework .CuratorFramework ;
30+ import org .apache .curator .utils .CloseableScheduledExecutorService ;
3231import org .apache .curator .utils .ThreadUtils ;
3332import org .apache .curator .utils .ZKPaths ;
3433import org .apache .zookeeper .CreateMode ;
@@ -61,13 +60,15 @@ public class PersistentTtlNode implements Closeable {
6160 public static final int DEFAULT_TOUCH_SCHEDULE_FACTOR = 2 ;
6261 public static final boolean DEFAULT_USE_PARENT_CREATION = true ;
6362
63+ @ VisibleForTesting
64+ static final String TOUCH_THREAD_NAME = "PersistentTtlNode" ;
65+
6466 private final Logger log = LoggerFactory .getLogger (getClass ());
6567 private final PersistentNode node ;
6668 private final CuratorFramework client ;
6769 private final long ttlMs ;
6870 private final int touchScheduleFactor ;
69- private final ScheduledExecutorService executorService ;
70- private final AtomicReference <Future <?>> futureRef = new AtomicReference <>();
71+ private final CloseableScheduledExecutorService closeableExecutorService ;
7172 private final String childPath ;
7273
7374 /**
@@ -79,7 +80,9 @@ public class PersistentTtlNode implements Closeable {
7980 public PersistentTtlNode (CuratorFramework client , String path , long ttlMs , byte [] initData ) {
8081 this (
8182 client ,
82- Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory ("PersistentTtlNode" )),
83+ new CloseableScheduledExecutorService (
84+ Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory (TOUCH_THREAD_NAME )),
85+ true ),
8386 path ,
8487 ttlMs ,
8588 initData ,
@@ -99,7 +102,9 @@ public PersistentTtlNode(
99102 CuratorFramework client , String path , long ttlMs , byte [] initData , boolean useParentCreation ) {
100103 this (
101104 client ,
102- Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory ("PersistentTtlNode" )),
105+ new CloseableScheduledExecutorService (
106+ Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory (TOUCH_THREAD_NAME )),
107+ true ),
103108 path ,
104109 ttlMs ,
105110 initData ,
@@ -128,7 +133,7 @@ public PersistentTtlNode(
128133 int touchScheduleFactor ) {
129134 this (
130135 client ,
131- executorService ,
136+ new CloseableScheduledExecutorService ( executorService ) ,
132137 path ,
133138 ttlMs ,
134139 initData ,
@@ -157,6 +162,26 @@ public PersistentTtlNode(
157162 String childNodeName ,
158163 int touchScheduleFactor ,
159164 boolean useParentCreation ) {
165+ this (
166+ client ,
167+ new CloseableScheduledExecutorService (executorService , false ),
168+ path ,
169+ ttlMs ,
170+ initData ,
171+ childNodeName ,
172+ touchScheduleFactor ,
173+ useParentCreation );
174+ }
175+
176+ private PersistentTtlNode (
177+ CuratorFramework client ,
178+ CloseableScheduledExecutorService closeableExecutorService ,
179+ String path ,
180+ long ttlMs ,
181+ byte [] initData ,
182+ String childNodeName ,
183+ int touchScheduleFactor ,
184+ boolean useParentCreation ) {
160185 this .client = Objects .requireNonNull (client , "client cannot be null" );
161186 this .ttlMs = ttlMs ;
162187 this .touchScheduleFactor = touchScheduleFactor ;
@@ -168,7 +193,7 @@ protected void deleteNode() {
168193 // NOP
169194 }
170195 };
171- this .executorService = Objects . requireNonNull ( executorService , "executorService cannot be null" ) ;
196+ this .closeableExecutorService = closeableExecutorService ;
172197 childPath = ZKPaths .makePath (Objects .requireNonNull (path , "path cannot be null" ), childNodeName );
173198 }
174199
@@ -198,9 +223,8 @@ void touch() {
198223 */
199224 public void start () {
200225 node .start ();
201- Future <?> future = executorService .scheduleAtFixedRate (
226+ closeableExecutorService .scheduleAtFixedRate (
202227 this ::touch , ttlMs / touchScheduleFactor , ttlMs / touchScheduleFactor , TimeUnit .MILLISECONDS );
203- futureRef .set (future );
204228 }
205229
206230 /**
@@ -238,17 +262,19 @@ public byte[] getData() {
238262 return node .getData ();
239263 }
240264
265+ @ VisibleForTesting
266+ CloseableScheduledExecutorService getCloseableScheduledExecutorService () {
267+ return closeableExecutorService ;
268+ }
269+
241270 /**
242271 * Call when you are done with the PersistentTtlNode. Note: the ZNode is <em>not</em> immediately
243272 * deleted. However, if no other PersistentTtlNode with the same path is running the node will get deleted
244273 * based on the ttl.
245274 */
246275 @ Override
247276 public void close () {
248- Future <?> future = futureRef .getAndSet (null );
249- if (future != null ) {
250- future .cancel (true );
251- }
277+ closeableExecutorService .close ();
252278 try {
253279 node .close ();
254280 } catch (IOException e ) {
0 commit comments