CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-curator--curator-framework

High-level API that greatly simplifies using ZooKeeper.

Pending
Overview
Eval results
Files

watchers.mddocs/

Watcher Management

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.

Capabilities

CuratorWatcher Interface

Curator's enhanced watcher interface that provides better exception handling compared to standard ZooKeeper watchers.

/**
 * Curator's watcher interface
 */
public interface CuratorWatcher {
    /**
     * Process a watched event
     * @param event the event
     * @throws Exception errors
     */
    void process(WatchedEvent event) throws Exception;
}

Usage Examples:

// Create a CuratorWatcher
CuratorWatcher watcher = new CuratorWatcher() {
    @Override
    public void process(WatchedEvent event) throws Exception {
        System.out.println("Watcher triggered: " + event.getType() + " " + event.getPath());
        
        switch (event.getType()) {
            case NodeCreated:
                System.out.println("Node created: " + event.getPath());
                break;
            case NodeDeleted:
                System.out.println("Node deleted: " + event.getPath());
                break;
            case NodeDataChanged:
                System.out.println("Node data changed: " + event.getPath());
                break;
            case NodeChildrenChanged:
                System.out.println("Node children changed: " + event.getPath());
                break;
        }
    }
};

// Use watcher with operations
byte[] data = client.getData()
                    .usingWatcher(watcher)
                    .forPath("/watched/path");

List<String> children = client.getChildren()
                              .usingWatcher(watcher)
                              .forPath("/watched/parent");

// One-time existence check with watcher
Stat stat = client.checkExists()
                  .usingWatcher(watcher)
                  .forPath("/may/not/exist");

// Lambda-style watcher
client.getData()
      .usingWatcher(event -> {
          System.out.println("Lambda watcher: " + event.getPath());
      })
      .forPath("/lambda/watched");

Watcher Removal Management

Interface for managing watcher removal and cleanup to prevent memory leaks and ensure proper resource management.

/**
 * Returns a facade that tracks watchers created and allows one-shot removal
 * @return facade
 */
WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework();

/**
 * CuratorFramework facade that tracks watchers for removal
 */
public interface WatcherRemoveCuratorFramework extends CuratorFramework {
    /**
     * Remove all tracked watchers
     */
    void removeWatchers();
}

/**
 * Start a remove watches builder (deprecated in favor of watchers())
 * @return builder object
 * @deprecated use watchers() in ZooKeeper 3.6+
 */
@Deprecated
RemoveWatchesBuilder watches();

/**
 * Builder for removing watchers
 */
public interface RemoveWatchesBuilder {
    /**
     * Remove watchers of specific type
     * @param type type of watchers to remove
     * @return builder
     */
    RemoveWatchesLocal ofType(RemoveWatchesType type);
    
    /**
     * Remove specific watcher
     * @param watcher watcher to remove
     * @return builder
     */
    BackgroundPathable<Void> usingWatcher(Watcher watcher);
    
    /**
     * Remove specific curator watcher
     * @param watcher curator watcher to remove
     * @return builder
     */
    BackgroundPathable<Void> usingWatcher(CuratorWatcher watcher);
    
    /**
     * Remove all watchers from path
     * @return builder
     */
    BackgroundPathable<Void> removeAll();
}

/**
 * Types of watchers to remove
 */
public enum RemoveWatchesType {
    /**
     * Remove any type of watcher
     */
    Any,
    
    /**
     * Remove only data watchers
     */
    Data,
    
    /**
     * Remove only child watchers
     */
    Child
}

/**
 * Interface for local watcher removal
 */
public interface RemoveWatchesLocal {
    /**
     * Remove watchers locally only (don't notify server)
     * @return builder
     */
    BackgroundPathable<Void> locally();
}

Usage Examples:

// Create watcher-tracking facade
WatcherRemoveCuratorFramework watcherClient = client.newWatcherRemoveCuratorFramework();

// Use watchers normally - they will be tracked
CuratorWatcher dataWatcher = event -> System.out.println("Data changed: " + event.getPath());
CuratorWatcher childWatcher = event -> System.out.println("Children changed: " + event.getPath());

watcherClient.getData()
            .usingWatcher(dataWatcher)
            .forPath("/tracked/data");

watcherClient.getChildren()
            .usingWatcher(childWatcher)
            .forPath("/tracked/parent");

// Remove all tracked watchers at once
watcherClient.removeWatchers();

// Remove specific watchers (deprecated API)
client.watches()
      .usingWatcher(dataWatcher)
      .forPath("/specific/path");

// Remove all data watchers from path
client.watches()
      .ofType(RemoveWatchesType.Data)
      .forPath("/data/path");

// Remove all watchers from path
client.watches()
      .removeAll()
      .forPath("/all/watchers/path");

// Remove watchers locally only (don't notify server)
client.watches()
      .ofType(RemoveWatchesType.Any)
      .locally()
      .forPath("/local/removal");

// Background watcher removal
client.watches()
      .usingWatcher(someWatcher)
      .inBackground((curatorFramework, curatorEvent) -> {
          System.out.println("Watcher removed from: " + curatorEvent.getPath());
      })
      .forPath("/background/removal");

Advanced Watcher Management (ZooKeeper 3.6+)

Enhanced watcher management for ZooKeeper 3.6+ with persistent watches and improved watcher lifecycle.

/**
 * Start a watch builder. Supported only when ZooKeeper JAR of version 3.6 or above is used
 * @return builder object
 * @throws IllegalStateException ZooKeeper JAR is 3.5 or below
 */
WatchesBuilder watchers();

/**
 * Builder for managing watchers (ZK 3.6+)
 */
public interface WatchesBuilder extends RemoveWatchesBuilder {
    /**
     * Add persistent watcher
     * @return builder
     */
    AddWatchBuilder add();
    
    /**
     * Remove persistent watcher
     * @return builder
     */
    RemoveWatchesBuilder remove();
}

/**
 * Builder for adding persistent watches
 */
public interface AddWatchBuilder extends AddWatchBuilder2 {
    /**
     * Add watcher with specific mode
     * @param mode add watch mode
     * @return builder
     */
    WatchPathable withMode(AddWatchMode mode);
}

public interface AddWatchBuilder2 extends 
    WatchPathable, 
    Backgroundable<WatchPathable> {
    
    /**
     * Use specific watcher
     * @param watcher watcher to use
     * @return builder
     */
    Pathable<Void> usingWatcher(Watcher watcher);
    
    /**
     * Use specific curator watcher
     * @param watcher curator watcher to use
     * @return builder
     */
    Pathable<Void> usingWatcher(CuratorWatcher watcher);
}

Usage Examples:

// Check if advanced watcher features are available
try {
    // Add persistent watcher (ZK 3.6+)
    CuratorWatcher persistentWatcher = event -> {
        System.out.println("Persistent watcher triggered: " + event.getType() + " " + event.getPath());
        // This watcher will continue to receive events until explicitly removed
    };
    
    client.watchers()
          .add()
          .usingWatcher(persistentWatcher)
          .forPath("/persistent/watch");
    
    // Remove persistent watcher
    client.watchers()
          .remove()
          .usingWatcher(persistentWatcher)
          .forPath("/persistent/watch");
    
    // Add persistent watcher with specific mode
    client.watchers()
          .add()
          .withMode(AddWatchMode.PERSISTENT_RECURSIVE)
          .usingWatcher(event -> {
              System.out.println("Recursive persistent watch: " + event.getPath());
          })
          .forPath("/recursive/watch");
          
} catch (IllegalStateException e) {
    System.out.println("Advanced watcher features require ZooKeeper 3.6+");
    // Fall back to traditional watchers
}

// Background persistent watcher management
client.watchers()
      .add()
      .inBackground((curatorFramework, curatorEvent) -> {
          System.out.println("Persistent watcher added for: " + curatorEvent.getPath());
      })
      .usingWatcher(persistentWatcher)
      .forPath("/background/persistent");

Watcher Best Practices and Patterns

Common patterns and best practices for watcher management in distributed applications.

/**
 * Curator can hold internal references to watchers that may inhibit garbage collection.
 * Call this method on watchers you are no longer interested in.
 * @param watcher the watcher
 * @deprecated As of ZooKeeper 3.5 Curator's recipes will handle removing watcher references
 */
@Deprecated
void clearWatcherReferences(Watcher watcher);

Usage Examples:

// Pattern 1: Self-renewing watcher
public class SelfRenewingWatcher implements CuratorWatcher {
    private final CuratorFramework client;
    private final String path;
    private volatile boolean active = true;
    
    public SelfRenewingWatcher(CuratorFramework client, String path) {
        this.client = client;
        this.path = path;
    }
    
    @Override
    public void process(WatchedEvent event) throws Exception {
        if (!active) return;
        
        System.out.println("Event: " + event.getType() + " " + event.getPath());
        
        // Re-register watcher for continuous monitoring
        if (event.getType() != Event.EventType.NodeDeleted) {
            client.getData()
                  .usingWatcher(this)
                  .inBackground()
                  .forPath(path);
        }
    }
    
    public void stop() {
        active = false;
    }
}

// Usage
SelfRenewingWatcher renewingWatcher = new SelfRenewingWatcher(client, "/monitored/path");
client.getData()
      .usingWatcher(renewingWatcher)
      .forPath("/monitored/path");

// Pattern 2: Watcher with proper cleanup
public class ManagedWatcherExample {
    private final WatcherRemoveCuratorFramework watcherClient;
    private final List<CuratorWatcher> activeWatchers = new ArrayList<>();
    
    public ManagedWatcherExample(CuratorFramework client) {
        this.watcherClient = client.newWatcherRemoveCuratorFramework();
    }
    
    public void addDataWatcher(String path) throws Exception {
        CuratorWatcher watcher = event -> {
            System.out.println("Data watcher: " + event.getPath());
        };
        
        activeWatchers.add(watcher);
        watcherClient.getData()
                    .usingWatcher(watcher)
                    .forPath(path);
    }
    
    public void addChildrenWatcher(String path) throws Exception {
        CuratorWatcher watcher = event -> {
            System.out.println("Children watcher: " + event.getPath());
        };
        
        activeWatchers.add(watcher);
        watcherClient.getChildren()
                    .usingWatcher(watcher)
                    .forPath(path);
    }
    
    public void cleanup() {
        // Remove all tracked watchers
        watcherClient.removeWatchers();
        activeWatchers.clear();
    }
}

// Pattern 3: Conditional watcher registration
public void conditionalWatcherExample() throws Exception {
    CuratorWatcher conditionalWatcher = event -> {
        System.out.println("Conditional event: " + event.getType());
        
        // Only re-register under certain conditions
        if (event.getType() == Event.EventType.NodeDataChanged) {
            try {
                // Check if we should continue watching
                Stat stat = client.checkExists().forPath(event.getPath());
                if (stat != null && stat.getDataLength() > 0) {
                    // Re-register watcher
                    client.getData()
                          .usingWatcher(this)
                          .inBackground()
                          .forPath(event.getPath());
                }
            } catch (Exception e) {
                System.err.println("Error re-registering watcher: " + e.getMessage());
            }
        }
    };
    
    // Initial registration
    client.getData()
          .usingWatcher(conditionalWatcher)
          .forPath("/conditional/path");
}

// Pattern 4: Batch watcher management
public void batchWatcherManagement() throws Exception {
    List<String> pathsToWatch = Arrays.asList("/path1", "/path2", "/path3");
    WatcherRemoveCuratorFramework batchClient = client.newWatcherRemoveCuratorFramework();
    
    // Add watchers for all paths
    for (String path : pathsToWatch) {
        CuratorWatcher watcher = event -> {
            System.out.println("Batch watcher for " + path + ": " + event.getType());
        };
        
        batchClient.getData()
                  .usingWatcher(watcher) 
                  .inBackground()
                  .forPath(path);
    }
    
    // Later, remove all watchers at once
    batchClient.removeWatchers();
}

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-curator--curator-framework

docs

client-factory.md

core-operations.md

events-state.md

index.md

schema-validation.md

transactions.md

watchers.md

tile.json