or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-factory.mdcore-operations.mdevents-state.mdindex.mdschema-validation.mdtransactions.mdwatchers.md

watchers.mddocs/

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

```