Commit eb97242a authored by rawan's avatar rawan

Initial commit - Distributed Search System with gRPC and ZooKeeper

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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Distributed_Search_System</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>com.search.project.Application</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<protobuf.version>3.25.1</protobuf.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.60.0</grpc.version>
</properties>
</project>
<?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>Distributed_Search_System</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>
<protobuf.version>3.25.1</protobuf.version>
<grpc.version>1.60.0</grpc.version> </properties>
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency> <groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal> </goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.search.project.Application</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.search.project;
import com.search.project.proto.SearchRequest;
import com.search.project.proto.SearchResponse;
import com.search.project.proto.SearchServiceGrpc;
import io.grpc.*;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.List;
import java.util.Scanner;
public class Application {
private static final String ZK_ADDRESS = "172.29.3.101:2181";
public static void main(String[] args) throws Exception {
CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, new ExponentialBackoffRetry(1000, 3));
client.start();
ServiceRegistry registry = new ServiceRegistry(client);
// بدء انتخاب القائد
registry.startLeaderElection(() -> {
System.out.println("[Coordinator] أنا الآن القائد والمنسق للنظام.");
new Thread(() -> startSearchInterface(client, registry)).start();
});
// تشغيل كعامل (Worker) في الخلفية دائماً
startWorkerNode(registry);
}
private static void startSearchInterface(CuratorFramework client, ServiceRegistry registry) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("\n>>> أدخل كلمة البحث (أو 'exit'): ");
String query = scanner.nextLine();
if (query.equalsIgnoreCase("exit")) break;
try {
List<String> workers = registry.getAllWorkers();
if (workers.isEmpty()) {
System.out.println("لا يوجد عمال متصلون حالياً.");
continue;
}
for (String workerNode : workers) {
byte[] data = client.getData().forPath("/service_registry/" + workerNode);
String address = new String(data);
String[] parts = address.split(":");
ManagedChannel channel = ManagedChannelBuilder.forAddress(parts[0], Integer.parseInt(parts[1]))
.usePlaintext().build();
SearchServiceGrpc.SearchServiceBlockingStub stub = SearchServiceGrpc.newBlockingStub(channel);
SearchResponse response = stub.processSearch(SearchRequest.newBuilder().setQuery(query).build());
System.out.println("\n[الرد من العامل " + parts[1] + "]:\n" + response.getResult());
channel.shutdown();
}
} catch (Exception e) {
System.out.println("خطأ أثناء البحث: " + e.getMessage());
}
}
}
private static void startWorkerNode(ServiceRegistry registry) throws Exception {
int port;
try (ServerSocket socket = new ServerSocket(0)) { port = socket.getLocalPort(); }
String myAddress = InetAddress.getLocalHost().getHostAddress() + ":" + port;
registry.registerToCluster(myAddress);
Server server = ServerBuilder.forPort(port)
.addService(new SearchServiceImplementation())
.build();
System.out.println("[Worker] السيرفر جاهز كعامل على البورت: " + port);
server.start();
server.awaitTermination();
}
}
\ No newline at end of file
package com.search.project;
import com.search.project.proto.SearchRequest;
import com.search.project.proto.SearchResponse;
import com.search.project.proto.SearchServiceGrpc;
import io.grpc.stub.StreamObserver;
import java.io.File;
import java.nio.file.Files;
import java.util.Arrays;
public class SearchServiceImplementation extends SearchServiceGrpc.SearchServiceImplBase {
private static final String DATA_DIR = "D:/search_data"; // تأكدي من وجود المجلد وبعض ملفات .txt
@Override
public void processSearch(SearchRequest request, StreamObserver<SearchResponse> responseObserver) {
String query = request.getQuery().toLowerCase();
StringBuilder results = new StringBuilder();
File dir = new File(DATA_DIR);
File[] files = dir.listFiles((d, name) -> name.endsWith(".txt"));
if (files != null) {
for (File file : files) {
try {
String content = Files.readString(file.toPath()).toLowerCase();
String[] words = content.split("\\W+");
long count = Arrays.stream(words).filter(w -> w.equals(query)).count();
double tf = (words.length > 0) ? (double) count / words.length : 0;
if (tf > 0) {
results.append(String.format("File: %-12s | Score: %.4f\n", file.getName(), tf));
}
} catch (Exception e) {
results.append("Error reading: ").append(file.getName()).append("\n");
}
}
}
String finalMsg = results.length() > 0 ? results.toString() : "No results found.";
responseObserver.onNext(SearchResponse.newBuilder().setResult(finalMsg).build());
responseObserver.onCompleted();
}
}
\ No newline at end of file
package com.search.project;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.zookeeper.CreateMode;
import java.net.InetAddress;
import java.util.List;
public class ServiceRegistry {
private static final String ELECTION_PATH = "/election";
private static final String REGISTRY_PATH = "/service_registry";
private final CuratorFramework client;
public ServiceRegistry(CuratorFramework client) {
this.client = client;
}
public void startLeaderElection(Runnable onLeadershipGained) {
LeaderSelector leaderSelector = new LeaderSelector(client, ELECTION_PATH, new LeaderSelectorListenerAdapter() {
@Override
public void takeLeadership(CuratorFramework client) throws Exception {
onLeadershipGained.run();
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
leaderSelector.autoRequeue();
leaderSelector.start();
}
public void registerToCluster(String metadata) throws Exception {
if (client.checkExists().forPath(REGISTRY_PATH) == null) {
client.create().creatingParentsIfNeeded().forPath(REGISTRY_PATH);
}
client.create()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(REGISTRY_PATH + "/worker_", metadata.getBytes());
}
public List<String> getAllWorkers() throws Exception {
if (client.checkExists().forPath(REGISTRY_PATH) == null) return List.of();
return client.getChildren().forPath(REGISTRY_PATH);
}
}
\ No newline at end of file
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
\ No newline at end of file
syntax = "proto3";
// هذا السطر مهم جداً ليتطابق مع بنية مجلداتك
option java_package = "com.search.project.proto";
option java_multiple_files = true;
package search;
service SearchService {
rpc processSearch(SearchRequest) returns (SearchResponse);
}
message SearchRequest {
string query = 1;
}
message SearchResponse {
string result = 1;
}
\ 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