0
# Watcher Management
1
2
Enhanced watcher lifecycle management with support for both traditional ZooKeeper watchers and newer persistent watches, including automatic cleanup and removal capabilities for reliable resource management.
3
4
## Capabilities
5
6
### CuratorWatcher Interface
7
8
Curator's enhanced watcher interface that provides better exception handling compared to standard ZooKeeper watchers.
9
10
```java { .api }
11
/**
12
* Curator's watcher interface
13
*/
14
public interface CuratorWatcher {
15
/**
16
* Process a watched event
17
* @param event the event
18
* @throws Exception errors
19
*/
20
void process(WatchedEvent event) throws Exception;
21
}
22
```
23
24
**Usage Examples:**
25
26
```java
27
// Create a CuratorWatcher
28
CuratorWatcher watcher = new CuratorWatcher() {
29
@Override
30
public void process(WatchedEvent event) throws Exception {
31
System.out.println("Watcher triggered: " + event.getType() + " " + event.getPath());
32
33
switch (event.getType()) {
34
case NodeCreated:
35
System.out.println("Node created: " + event.getPath());
36
break;
37
case NodeDeleted:
38
System.out.println("Node deleted: " + event.getPath());
39
break;
40
case NodeDataChanged:
41
System.out.println("Node data changed: " + event.getPath());
42
break;
43
case NodeChildrenChanged:
44
System.out.println("Node children changed: " + event.getPath());
45
break;
46
}
47
}
48
};
49
50
// Use watcher with operations
51
byte[] data = client.getData()
52
.usingWatcher(watcher)
53
.forPath("/watched/path");
54
55
List<String> children = client.getChildren()
56
.usingWatcher(watcher)
57
.forPath("/watched/parent");
58
59
// One-time existence check with watcher
60
Stat stat = client.checkExists()
61
.usingWatcher(watcher)
62
.forPath("/may/not/exist");
63
64
// Lambda-style watcher
65
client.getData()
66
.usingWatcher(event -> {
67
System.out.println("Lambda watcher: " + event.getPath());
68
})
69
.forPath("/lambda/watched");
70
```
71
72
### Watcher Removal Management
73
74
Interface for managing watcher removal and cleanup to prevent memory leaks and ensure proper resource management.
75
76
```java { .api }
77
/**
78
* Returns a facade that tracks watchers created and allows one-shot removal
79
* @return facade
80
*/
81
WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework();
82
83
/**
84
* CuratorFramework facade that tracks watchers for removal
85
*/
86
public interface WatcherRemoveCuratorFramework extends CuratorFramework {
87
/**
88
* Remove all tracked watchers
89
*/
90
void removeWatchers();
91
}
92
93
/**
94
* Start a remove watches builder (deprecated in favor of watchers())
95
* @return builder object
96
* @deprecated use watchers() in ZooKeeper 3.6+
97
*/
98
@Deprecated
99
RemoveWatchesBuilder watches();
100
101
/**
102
* Builder for removing watchers
103
*/
104
public interface RemoveWatchesBuilder {
105
/**
106
* Remove watchers of specific type
107
* @param type type of watchers to remove
108
* @return builder
109
*/
110
RemoveWatchesLocal ofType(RemoveWatchesType type);
111
112
/**
113
* Remove specific watcher
114
* @param watcher watcher to remove
115
* @return builder
116
*/
117
BackgroundPathable<Void> usingWatcher(Watcher watcher);
118
119
/**
120
* Remove specific curator watcher
121
* @param watcher curator watcher to remove
122
* @return builder
123
*/
124
BackgroundPathable<Void> usingWatcher(CuratorWatcher watcher);
125
126
/**
127
* Remove all watchers from path
128
* @return builder
129
*/
130
BackgroundPathable<Void> removeAll();
131
}
132
133
/**
134
* Types of watchers to remove
135
*/
136
public enum RemoveWatchesType {
137
/**
138
* Remove any type of watcher
139
*/
140
Any,
141
142
/**
143
* Remove only data watchers
144
*/
145
Data,
146
147
/**
148
* Remove only child watchers
149
*/
150
Child
151
}
152
153
/**
154
* Interface for local watcher removal
155
*/
156
public interface RemoveWatchesLocal {
157
/**
158
* Remove watchers locally only (don't notify server)
159
* @return builder
160
*/
161
BackgroundPathable<Void> locally();
162
}
163
```
164
165
**Usage Examples:**
166
167
```java
168
// Create watcher-tracking facade
169
WatcherRemoveCuratorFramework watcherClient = client.newWatcherRemoveCuratorFramework();
170
171
// Use watchers normally - they will be tracked
172
CuratorWatcher dataWatcher = event -> System.out.println("Data changed: " + event.getPath());
173
CuratorWatcher childWatcher = event -> System.out.println("Children changed: " + event.getPath());
174
175
watcherClient.getData()
176
.usingWatcher(dataWatcher)
177
.forPath("/tracked/data");
178
179
watcherClient.getChildren()
180
.usingWatcher(childWatcher)
181
.forPath("/tracked/parent");
182
183
// Remove all tracked watchers at once
184
watcherClient.removeWatchers();
185
186
// Remove specific watchers (deprecated API)
187
client.watches()
188
.usingWatcher(dataWatcher)
189
.forPath("/specific/path");
190
191
// Remove all data watchers from path
192
client.watches()
193
.ofType(RemoveWatchesType.Data)
194
.forPath("/data/path");
195
196
// Remove all watchers from path
197
client.watches()
198
.removeAll()
199
.forPath("/all/watchers/path");
200
201
// Remove watchers locally only (don't notify server)
202
client.watches()
203
.ofType(RemoveWatchesType.Any)
204
.locally()
205
.forPath("/local/removal");
206
207
// Background watcher removal
208
client.watches()
209
.usingWatcher(someWatcher)
210
.inBackground((curatorFramework, curatorEvent) -> {
211
System.out.println("Watcher removed from: " + curatorEvent.getPath());
212
})
213
.forPath("/background/removal");
214
```
215
216
### Advanced Watcher Management (ZooKeeper 3.6+)
217
218
Enhanced watcher management for ZooKeeper 3.6+ with persistent watches and improved watcher lifecycle.
219
220
```java { .api }
221
/**
222
* Start a watch builder. Supported only when ZooKeeper JAR of version 3.6 or above is used
223
* @return builder object
224
* @throws IllegalStateException ZooKeeper JAR is 3.5 or below
225
*/
226
WatchesBuilder watchers();
227
228
/**
229
* Builder for managing watchers (ZK 3.6+)
230
*/
231
public interface WatchesBuilder extends RemoveWatchesBuilder {
232
/**
233
* Add persistent watcher
234
* @return builder
235
*/
236
AddWatchBuilder add();
237
238
/**
239
* Remove persistent watcher
240
* @return builder
241
*/
242
RemoveWatchesBuilder remove();
243
}
244
245
/**
246
* Builder for adding persistent watches
247
*/
248
public interface AddWatchBuilder extends AddWatchBuilder2 {
249
/**
250
* Add watcher with specific mode
251
* @param mode add watch mode
252
* @return builder
253
*/
254
WatchPathable withMode(AddWatchMode mode);
255
}
256
257
public interface AddWatchBuilder2 extends
258
WatchPathable,
259
Backgroundable<WatchPathable> {
260
261
/**
262
* Use specific watcher
263
* @param watcher watcher to use
264
* @return builder
265
*/
266
Pathable<Void> usingWatcher(Watcher watcher);
267
268
/**
269
* Use specific curator watcher
270
* @param watcher curator watcher to use
271
* @return builder
272
*/
273
Pathable<Void> usingWatcher(CuratorWatcher watcher);
274
}
275
```
276
277
**Usage Examples:**
278
279
```java
280
// Check if advanced watcher features are available
281
try {
282
// Add persistent watcher (ZK 3.6+)
283
CuratorWatcher persistentWatcher = event -> {
284
System.out.println("Persistent watcher triggered: " + event.getType() + " " + event.getPath());
285
// This watcher will continue to receive events until explicitly removed
286
};
287
288
client.watchers()
289
.add()
290
.usingWatcher(persistentWatcher)
291
.forPath("/persistent/watch");
292
293
// Remove persistent watcher
294
client.watchers()
295
.remove()
296
.usingWatcher(persistentWatcher)
297
.forPath("/persistent/watch");
298
299
// Add persistent watcher with specific mode
300
client.watchers()
301
.add()
302
.withMode(AddWatchMode.PERSISTENT_RECURSIVE)
303
.usingWatcher(event -> {
304
System.out.println("Recursive persistent watch: " + event.getPath());
305
})
306
.forPath("/recursive/watch");
307
308
} catch (IllegalStateException e) {
309
System.out.println("Advanced watcher features require ZooKeeper 3.6+");
310
// Fall back to traditional watchers
311
}
312
313
// Background persistent watcher management
314
client.watchers()
315
.add()
316
.inBackground((curatorFramework, curatorEvent) -> {
317
System.out.println("Persistent watcher added for: " + curatorEvent.getPath());
318
})
319
.usingWatcher(persistentWatcher)
320
.forPath("/background/persistent");
321
```
322
323
### Watcher Best Practices and Patterns
324
325
Common patterns and best practices for watcher management in distributed applications.
326
327
```java { .api }
328
/**
329
* Curator can hold internal references to watchers that may inhibit garbage collection.
330
* Call this method on watchers you are no longer interested in.
331
* @param watcher the watcher
332
* @deprecated As of ZooKeeper 3.5 Curator's recipes will handle removing watcher references
333
*/
334
@Deprecated
335
void clearWatcherReferences(Watcher watcher);
336
```
337
338
**Usage Examples:**
339
340
```java
341
// Pattern 1: Self-renewing watcher
342
public class SelfRenewingWatcher implements CuratorWatcher {
343
private final CuratorFramework client;
344
private final String path;
345
private volatile boolean active = true;
346
347
public SelfRenewingWatcher(CuratorFramework client, String path) {
348
this.client = client;
349
this.path = path;
350
}
351
352
@Override
353
public void process(WatchedEvent event) throws Exception {
354
if (!active) return;
355
356
System.out.println("Event: " + event.getType() + " " + event.getPath());
357
358
// Re-register watcher for continuous monitoring
359
if (event.getType() != Event.EventType.NodeDeleted) {
360
client.getData()
361
.usingWatcher(this)
362
.inBackground()
363
.forPath(path);
364
}
365
}
366
367
public void stop() {
368
active = false;
369
}
370
}
371
372
// Usage
373
SelfRenewingWatcher renewingWatcher = new SelfRenewingWatcher(client, "/monitored/path");
374
client.getData()
375
.usingWatcher(renewingWatcher)
376
.forPath("/monitored/path");
377
378
// Pattern 2: Watcher with proper cleanup
379
public class ManagedWatcherExample {
380
private final WatcherRemoveCuratorFramework watcherClient;
381
private final List<CuratorWatcher> activeWatchers = new ArrayList<>();
382
383
public ManagedWatcherExample(CuratorFramework client) {
384
this.watcherClient = client.newWatcherRemoveCuratorFramework();
385
}
386
387
public void addDataWatcher(String path) throws Exception {
388
CuratorWatcher watcher = event -> {
389
System.out.println("Data watcher: " + event.getPath());
390
};
391
392
activeWatchers.add(watcher);
393
watcherClient.getData()
394
.usingWatcher(watcher)
395
.forPath(path);
396
}
397
398
public void addChildrenWatcher(String path) throws Exception {
399
CuratorWatcher watcher = event -> {
400
System.out.println("Children watcher: " + event.getPath());
401
};
402
403
activeWatchers.add(watcher);
404
watcherClient.getChildren()
405
.usingWatcher(watcher)
406
.forPath(path);
407
}
408
409
public void cleanup() {
410
// Remove all tracked watchers
411
watcherClient.removeWatchers();
412
activeWatchers.clear();
413
}
414
}
415
416
// Pattern 3: Conditional watcher registration
417
public void conditionalWatcherExample() throws Exception {
418
CuratorWatcher conditionalWatcher = event -> {
419
System.out.println("Conditional event: " + event.getType());
420
421
// Only re-register under certain conditions
422
if (event.getType() == Event.EventType.NodeDataChanged) {
423
try {
424
// Check if we should continue watching
425
Stat stat = client.checkExists().forPath(event.getPath());
426
if (stat != null && stat.getDataLength() > 0) {
427
// Re-register watcher
428
client.getData()
429
.usingWatcher(this)
430
.inBackground()
431
.forPath(event.getPath());
432
}
433
} catch (Exception e) {
434
System.err.println("Error re-registering watcher: " + e.getMessage());
435
}
436
}
437
};
438
439
// Initial registration
440
client.getData()
441
.usingWatcher(conditionalWatcher)
442
.forPath("/conditional/path");
443
}
444
445
// Pattern 4: Batch watcher management
446
public void batchWatcherManagement() throws Exception {
447
List<String> pathsToWatch = Arrays.asList("/path1", "/path2", "/path3");
448
WatcherRemoveCuratorFramework batchClient = client.newWatcherRemoveCuratorFramework();
449
450
// Add watchers for all paths
451
for (String path : pathsToWatch) {
452
CuratorWatcher watcher = event -> {
453
System.out.println("Batch watcher for " + path + ": " + event.getType());
454
};
455
456
batchClient.getData()
457
.usingWatcher(watcher)
458
.inBackground()
459
.forPath(path);
460
}
461
462
// Later, remove all watchers at once
463
batchClient.removeWatchers();
464
}
465
```