24
24
import java .io .IOException ;
25
25
import java .util .Objects ;
26
26
import java .util .concurrent .Executors ;
27
- import java .util .concurrent .Future ;
28
27
import java .util .concurrent .ScheduledExecutorService ;
29
28
import java .util .concurrent .TimeUnit ;
30
- import java .util .concurrent .atomic .AtomicReference ;
31
29
import org .apache .curator .framework .CuratorFramework ;
30
+ import org .apache .curator .utils .CloseableScheduledExecutorService ;
32
31
import org .apache .curator .utils .ThreadUtils ;
33
32
import org .apache .curator .utils .ZKPaths ;
34
33
import org .apache .zookeeper .CreateMode ;
@@ -61,13 +60,15 @@ public class PersistentTtlNode implements Closeable {
61
60
public static final int DEFAULT_TOUCH_SCHEDULE_FACTOR = 2 ;
62
61
public static final boolean DEFAULT_USE_PARENT_CREATION = true ;
63
62
63
+ @ VisibleForTesting
64
+ static final String TOUCH_THREAD_NAME = "PersistentTtlNode" ;
65
+
64
66
private final Logger log = LoggerFactory .getLogger (getClass ());
65
67
private final PersistentNode node ;
66
68
private final CuratorFramework client ;
67
69
private final long ttlMs ;
68
70
private final int touchScheduleFactor ;
69
- private final ScheduledExecutorService executorService ;
70
- private final AtomicReference <Future <?>> futureRef = new AtomicReference <>();
71
+ private final CloseableScheduledExecutorService closeableExecutorService ;
71
72
private final String childPath ;
72
73
73
74
/**
@@ -79,7 +80,9 @@ public class PersistentTtlNode implements Closeable {
79
80
public PersistentTtlNode (CuratorFramework client , String path , long ttlMs , byte [] initData ) {
80
81
this (
81
82
client ,
82
- Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory ("PersistentTtlNode" )),
83
+ new CloseableScheduledExecutorService (
84
+ Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory (TOUCH_THREAD_NAME )),
85
+ true ),
83
86
path ,
84
87
ttlMs ,
85
88
initData ,
@@ -99,7 +102,9 @@ public PersistentTtlNode(
99
102
CuratorFramework client , String path , long ttlMs , byte [] initData , boolean useParentCreation ) {
100
103
this (
101
104
client ,
102
- Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory ("PersistentTtlNode" )),
105
+ new CloseableScheduledExecutorService (
106
+ Executors .newSingleThreadScheduledExecutor (ThreadUtils .newThreadFactory (TOUCH_THREAD_NAME )),
107
+ true ),
103
108
path ,
104
109
ttlMs ,
105
110
initData ,
@@ -128,7 +133,7 @@ public PersistentTtlNode(
128
133
int touchScheduleFactor ) {
129
134
this (
130
135
client ,
131
- executorService ,
136
+ new CloseableScheduledExecutorService ( executorService ) ,
132
137
path ,
133
138
ttlMs ,
134
139
initData ,
@@ -157,6 +162,26 @@ public PersistentTtlNode(
157
162
String childNodeName ,
158
163
int touchScheduleFactor ,
159
164
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 ) {
160
185
this .client = Objects .requireNonNull (client , "client cannot be null" );
161
186
this .ttlMs = ttlMs ;
162
187
this .touchScheduleFactor = touchScheduleFactor ;
@@ -168,7 +193,7 @@ protected void deleteNode() {
168
193
// NOP
169
194
}
170
195
};
171
- this .executorService = Objects . requireNonNull ( executorService , "executorService cannot be null" ) ;
196
+ this .closeableExecutorService = closeableExecutorService ;
172
197
childPath = ZKPaths .makePath (Objects .requireNonNull (path , "path cannot be null" ), childNodeName );
173
198
}
174
199
@@ -198,9 +223,8 @@ void touch() {
198
223
*/
199
224
public void start () {
200
225
node .start ();
201
- Future <?> future = executorService .scheduleAtFixedRate (
226
+ closeableExecutorService .scheduleAtFixedRate (
202
227
this ::touch , ttlMs / touchScheduleFactor , ttlMs / touchScheduleFactor , TimeUnit .MILLISECONDS );
203
- futureRef .set (future );
204
228
}
205
229
206
230
/**
@@ -238,17 +262,19 @@ public byte[] getData() {
238
262
return node .getData ();
239
263
}
240
264
265
+ @ VisibleForTesting
266
+ CloseableScheduledExecutorService getCloseableScheduledExecutorService () {
267
+ return closeableExecutorService ;
268
+ }
269
+
241
270
/**
242
271
* Call when you are done with the PersistentTtlNode. Note: the ZNode is <em>not</em> immediately
243
272
* deleted. However, if no other PersistentTtlNode with the same path is running the node will get deleted
244
273
* based on the ttl.
245
274
*/
246
275
@ Override
247
276
public void close () {
248
- Future <?> future = futureRef .getAndSet (null );
249
- if (future != null ) {
250
- future .cancel (true );
251
- }
277
+ closeableExecutorService .close ();
252
278
try {
253
279
node .close ();
254
280
} catch (IOException e ) {
0 commit comments