Commit d295f4c7 authored by rawan's avatar rawan

Initial commit: Leader Election and Watchers projects

parents
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
\ No newline at end of file
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>leader-election</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.9.1</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package org.example;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class ImprovedLeaderElection implements Watcher {
// Make sure to update this address to match your ZooKeeper server address
private static final String address = "192.168.184.103:2181";
private static final int SESSION_TIMEOUT = 3000;
private static final String ELECTION_NAMESPACE = "/election";
private static final String ZNODE_PREFIX = ELECTION_NAMESPACE + "/c_";
private String currentZnodeName;
private ZooKeeper zooKeeper;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
ImprovedLeaderElection leaderElection = new ImprovedLeaderElection();
leaderElection.connectToZookeeper();
// 1. Volunteer to create the candidacy node
leaderElection.volunteerForLeadership();
// 2. Apply the Sequential Watching algorithm
leaderElection.electLeader();
leaderElection.run();
leaderElection.close();
System.out.println("Closed successfully");
}
public void volunteerForLeadership() throws InterruptedException, KeeperException {
// Create an Ephemeral Sequential znode
String znodeFullPath = zooKeeper.create(ZNODE_PREFIX, new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("Candidacy Znode created: " + znodeFullPath);
this.currentZnodeName = znodeFullPath.replace(ELECTION_NAMESPACE + "/", "");
}
/**
* The core function of the Sequential Watching algorithm.
* Each node places a Watcher only on the node immediately preceding it.
*/
public void electLeader() throws InterruptedException, KeeperException {
// 1. Get the list of children without setting a watcher (false)
List<String> children = zooKeeper.getChildren(ELECTION_NAMESPACE, false);
Collections.sort(children);
String smallestChild = children.get(0); // Potential Leader
int myIndex = children.indexOf(currentZnodeName);
if (smallestChild.equals(currentZnodeName)) {
// I am the Leader: I monitor nothing
System.out.println("\n-----------------------------------------");
System.out.println("I am the LEADER: " + currentZnodeName);
System.out.println("-----------------------------------------\n");
} else {
// I am a Follower: I must monitor only the node preceding me
String smallerSiblingName = children.get(myIndex - 1);
String smallerSiblingPath = ELECTION_NAMESPACE + "/" + smallerSiblingName;
// **We only set a watcher on the previous node via exists**
// The watcher is 'this', and will receive a NodeDeleted notification when the previous node falls (Herd Effect broken)
Stat stat = zooKeeper.exists(smallerSiblingPath, this);
if (stat == null) {
// The previous node fell before the watcher was set, re-elect
System.out.println("The previous node does not exist. Re-election...");
electLeader();
} else {
System.out.println("I am a Follower. I monitor the previous node: " + smallerSiblingName);
}
}
}
private void close() throws InterruptedException {
this.zooKeeper.close();
}
public void connectToZookeeper() throws IOException {
this.zooKeeper = new ZooKeeper(address, SESSION_TIMEOUT, this);
}
public void run() throws InterruptedException {
synchronized (zooKeeper) {
zooKeeper.wait();
}
}
/**
* Event processing: Re-election is triggered only upon receiving a NodeDeleted notification.
*/
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case None:
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("Connected to ZooKeeper successfully");
} else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
synchronized (zooKeeper) {
System.out.println("The connection to ZooKeeper has been lost");
zooKeeper.notifyAll();
}
} else if (watchedEvent.getState() == Event.KeeperState.Closed) {
System.out.println("Closed successfully");
}
break;
// The only event that should trigger the re-election process
case NodeDeleted:
try {
// Only the node monitoring the deleted node will reach here,
// which breaks the synchronization (Herd Effect)
System.out.println("--- I was notified: The previous node has been deleted. Re-election... ---");
electLeader();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (KeeperException e) {
throw new RuntimeException(e);
}
break;
default:
// Ignore any other events that do not serve the election algorithm
break;
}
}
}
\ No newline at end of file
package org.example;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.List;
public class WatchersAndTriggers implements Watcher {
private static final String address = "192.168.184.101:2181";
private static final int SESSION_TIMEOUT = 3000; // Dead client timeout
private static final String TargetZnode = "/target_znode";
private ZooKeeper zooKeeper;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
WatchersAndTriggers watchersAndTriggers = new WatchersAndTriggers();
watchersAndTriggers.connectToZookeeper();
watchersAndTriggers.initialSetupAndWatchers();
watchersAndTriggers.run();
watchersAndTriggers.close();
System.out.println("Closed Successfully");
}
private void close() throws InterruptedException {
this.zooKeeper.close();
}
private void run() throws InterruptedException {
synchronized (zooKeeper) {
this.zooKeeper.wait();
}
}
private void connectToZookeeper() throws IOException {
this.zooKeeper = new ZooKeeper(address, SESSION_TIMEOUT, this);
}
/**
* Function to set initial watchers (Initial Setup).
*/
public void initialSetupAndWatchers() throws InterruptedException, KeeperException {
// 1. Exist Watch: using "exists"
Stat stat = zooKeeper.exists(TargetZnode, this);
if (stat == null) {
System.out.println(TargetZnode + " not exist. Waiting for creation.");
return;
}
// 2. Data Watch: using "getData"
byte[] data = zooKeeper.getData(TargetZnode, this, stat);
// 3. Children Watch: using "getChildren"
List<String> children = zooKeeper.getChildren(TargetZnode, this);
System.out.println("Data: " + new String(data) + ", Children: " + children);
}
/**
* The modified event processing function (PROCESS).
* Selectively re-arms only the triggered watcher to avoid the race condition.
*/
@Override
public void process(WatchedEvent watchedEvent) {
try {
switch (watchedEvent.getType()) {
case None:
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("Successfully connected to Zookeeper");
} else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
synchronized (zooKeeper) {
System.out.println("Disconnected from Zookeeper");
zooKeeper.notifyAll();
}
} else if (watchedEvent.getState() == Event.KeeperState.Closed) {
System.out.println("Closed Successfully");
}
break;
case NodeCreated:
System.out.println(TargetZnode + " Created");
// On creation: re-arm all watchers (Data, Children, and Exist)
// and fetch current data/children
rearmAllWatchersAfterCreation();
break;
case NodeDeleted:
System.out.println(TargetZnode + " Deleted");
// On deletion: re-arm only the Exist Watch (in case the znode is recreated)
zooKeeper.exists(TargetZnode, this);
break;
case NodeDataChanged:
System.out.println(TargetZnode + " DataChanged");
// Re-arm only the Data Watch
zooKeeper.getData(TargetZnode, this, new Stat());
break;
case NodeChildrenChanged:
System.out.println(TargetZnode + " ChildrenChanged");
// Re-arm only the Children Watch
zooKeeper.getChildren(TargetZnode, this);
break;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (KeeperException e) {
// Handle exceptions
System.err.println("KeeperException during re-arming watchers: " + e.getMessage());
}
}
/**
* Helper function to get data and re-arm all watchers after node creation,
* ensuring the latest state is captured.
*/
private void rearmAllWatchersAfterCreation() throws InterruptedException, KeeperException {
Stat stat = zooKeeper.exists(TargetZnode, this); // Re-arm Exist Watch
if (stat != null) {
byte[] data = zooKeeper.getData(TargetZnode, this, stat); // Re-arm Data Watch
List<String> children = zooKeeper.getChildren(TargetZnode, this); // Re-arm Children Watch
System.out.println("New Data: " + new String(data) + ", New Children: " + children);
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment