Commit a8f0b211 authored by tammam.alsoleman's avatar tammam.alsoleman

create the FrontendApplication

parent d1b92d05
package com.distributed.search;
import com.distributed.search.cluster.LeaderHttpRegistry;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Executors;
public class FrontendApplication implements Watcher {
private static final String ZOOKEEPER_ADDRESS = "192.168.39.250:2181"; // Use your ZK IP
private static final int PORT = 8080; // Main Web Port
private final LeaderHttpRegistry leaderHttpRegistry;
private final HttpClient httpClient;
public FrontendApplication(ZooKeeper zooKeeper) {
this.leaderHttpRegistry = new LeaderHttpRegistry(zooKeeper);
this.httpClient = HttpClient.newBuilder().build();
}
public static void main(String[] args) throws Exception {
ZooKeeper zooKeeper = new ZooKeeper(ZOOKEEPER_ADDRESS, 3000, event -> {});
FrontendApplication app = new FrontendApplication(zooKeeper);
app.startServer();
}
public void startServer() throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
// Context 1: Serve the HTML UI
server.createContext("/", this::handleHomeRequest);
// Context 2: API Proxy to Leader
server.createContext("/api/search", this::handleSearchApiRequest);
server.setExecutor(Executors.newFixedThreadPool(8));
server.start();
System.out.println("Frontend Server is running at http://localhost:" + PORT);
}
// Serves the index.html file to the browser
private void handleHomeRequest(HttpExchange exchange) throws IOException {
byte[] response = Files.readAllBytes(Paths.get("src/main/resources/index.html"));
exchange.getResponseHeaders().set("Content-Type", "text/html");
sendResponse(response, exchange);
}
// Receives query from browser -> Finds Leader -> Fetches results from Leader -> Returns to browser
private void handleSearchApiRequest(HttpExchange exchange) throws IOException {
// 1. Only support GET requests
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, -1);
return;
}
// 2. Get the RAW query from browser (e.g., query=blue%20car)
String query = exchange.getRequestURI().getRawQuery();
String leaderUrl = leaderHttpRegistry.getLeaderAddress();
if (leaderUrl == null) {
sendPlainTextResponse("Leader not found in registry", exchange, 502);
return;
}
try {
// 3. Robust URL Construction
// Ensure leaderUrl has protocol and the correct endpoint
if (!leaderUrl.startsWith("http")) leaderUrl = "http://" + leaderUrl;
if (!leaderUrl.contains("/search")) leaderUrl = leaderUrl.replaceAll("/$", "") + "/search";
// Create the final URI carefully
// concatenation of (base) + (?) + (already encoded query)
URI uri = URI.create(leaderUrl + "?" + query);
System.out.println("Frontend Proxying to Leader: " + uri);
// 4. Prepare the HTTP Request to the Leader
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.GET()
.timeout(java.time.Duration.ofSeconds(30))
.build();
// 5. Send request and get response as InputStream (Efficiency)
HttpResponse<java.io.InputStream> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
int statusCode = resp.statusCode();
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
if (statusCode != 200) {
byte[] errorBytes = resp.body().readAllBytes();
sendPlainTextResponse(new String(errorBytes), exchange, statusCode);
return;
}
// 6. Stream the results directly from Leader to Browser
try (java.io.InputStream is = resp.body();
OutputStream os = exchange.getResponseBody()) {
// Send headers first (200 OK, 0 means chunked/unknown length)
exchange.sendResponseHeaders(200, 0);
is.transferTo(os); // Copy data directly
os.flush();
}
} catch (IllegalArgumentException e) {
System.err.println("Invalid Query format: " + e.getMessage());
sendPlainTextResponse("Invalid search format", exchange, 400);
} catch (Exception e) {
e.printStackTrace();
sendPlainTextResponse("Error communicating with Leader: " + e.getMessage(), exchange, 502);
}
}
private void sendPlainTextResponse(String text, HttpExchange exchange, int status) throws IOException {
byte[] bytes = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
exchange.sendResponseHeaders(status, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
}
private void sendResponse(byte[] bytes, HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
}
@Override
public void process(WatchedEvent event) {}
}
\ 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