Commit c5a013c5 authored by shouib.sobh's avatar shouib.sobh

Implement async WebClient and Aggregator

parent 355a464e
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="httpclient" />
<module name="httpserver" />
</profile>
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/httpclient/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/httpclient/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/httpserver/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/httpserver/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="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</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$/httpclient/pom.xml" />
<option value="$PROJECT_DIR$/httpserver/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="27" 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="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/network-communication.iml" filepath="$PROJECT_DIR$/.idea/network-communication.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class Aggregator {
......@@ -11,17 +13,32 @@ public class Aggregator {
/*send task to list of workers*/
public List<String> sendTasksToWorkers(List<String> workersAddresses, List<String> tasks) {
/*
Add your code here
*/
throw new UnsupportedOperationException();
// Reuse the headers version, passing null for "no headers"
return sendTasksToWorkers(workersAddresses, tasks, null);
}
/*send task to list of workers*/
public List<String> sendTasksToWorkers(List<String> workersAddresses, List<String> tasks, String headers) {
/*
Add your code here
*/
throw new UnsupportedOperationException();
// Each worker needs exactly one task, so the sizes must match
if (workersAddresses.size() != tasks.size()) {
throw new IllegalArgumentException(
"Number of workers (" + workersAddresses.size() +
") does not match number of tasks (" + tasks.size() + ")");
}
// Step 1: fire ALL requests first, without waiting for any response
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < workersAddresses.size(); i++) {
futures.add(webClient.sendTask(
workersAddresses.get(i),
tasks.get(i).getBytes(),
headers));
}
// Step 2: wait for the results; join() blocks until each future completes,
// and the stream keeps the same order the tasks were sent in
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
\ No newline at end of file
import java.net.URI;
import java.net.http.HttpClient;
import java.util.ArrayList;
import java.util.List;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class WebClient {
......@@ -15,17 +16,39 @@ public class WebClient {
/* send task (post http request) asynchronously */
public CompletableFuture<String> sendTask(String url, byte[] requestPayload) {
/*
Add your code here
*/
throw new UnsupportedOperationException();
// Build a POST request: the body is requestPayload, the target is url
HttpRequest request = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofByteArray(requestPayload))
.uri(URI.create(url))
.build();
// Send asynchronously: returns immediately without waiting for the response,
// then extract only the response body once it arrives
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
/* send task (post http request) asynchronously with custom headers*/
public CompletableFuture<String> sendTask(String url, byte[] requestPayload, String headers) {
/*
Add your code here
*/
throw new UnsupportedOperationException();
// Same request as above, but we don't call build() yet because we may add a header first
HttpRequest.Builder builder = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofByteArray(requestPayload))
.uri(URI.create(url));
// Add the header only if one was provided
if (headers != null && !headers.isBlank()) {
if (headers.contains(":")) {
// Full format, e.g. "Debug-X: true"
String[] parts = headers.split(":", 2);
builder.header(parts[0].trim(), parts[1].trim());
} else {
// Short format, e.g. "true" becomes the value of Debug-X
builder.header("X-Debug", headers.trim());
}
}
// Send the request and return the response body asynchronously
return client.sendAsync(builder.build(), HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="HOME_LOG" value="logs/app.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${HOME_LOG}</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</encoder>
</appender>
<!-- Log Level: ERROR, WARN, INFO, DEBUG, TRACE-->
<logger name="Server" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</logger>
<root level="error">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
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