Communication Client / Server HTTP

Classe MyHttpClient effectuant une requete synchrone et asynchrone au Server MyHttpServer qui renvoie une réponse

package fr.dyma;

import com.fasterxml.jackson.core.type.TypeReference;
import fr.dyma.model.Article;
import fr.dyma.model.Client;
import fr.dyma.service.util.ObjectJsonMapper;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;

import static java.lang.StringTemplate.STR;

public class MyHttpClient {
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {

        var articleJsonMapper = new ObjectJsonMapper<Article>();
        Article articleClient = new Article("article client", "From client", ":D", OffsetDateTime.now());
        var articleClientJson = articleJsonMapper.toJson(articleClient);

        var clientJsonMapper = new ObjectJsonMapper<Client>();
        Client clientClient = new Client("Tuan", "NGUYEN", "tuanounet@gmail.com");
        var clientClientJson = clientJsonMapper.toJson(clientClient);

        // HttpClient client = HttpClient.newBuilder()
        //        .version(HttpClient.Version.HTTP_1_1)
        //        .build();
        // HttpClient client = HttpClient.newHttpClient();

        HttpRequest requetePost = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8000/obj"))
                .setHeader("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(clientClientJson))
                .timeout(Duration.ofSeconds(20)) // Duration.of(20, ChronoUnit.SECONDS)
                .build();

        // Réponse synchrone
        try {
            HttpResponse response = HttpClient.newHttpClient().send(requetePost, HttpResponse.BodyHandlers.ofString());
            printInfoServer(articleJsonMapper, response);
            System.out.println("END OF SYNCHRONOUS SERVER RESPONSE");

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

        // Réponse asynchrone
        CompletableFuture<Void> responseAsync = HttpClient.newHttpClient().sendAsync(requetePost, HttpResponse.BodyHandlers.ofString()).thenAcceptAsync(
                resp -> {
                    printInfoServer(articleJsonMapper, resp);
                    System.out.println(Thread.currentThread().getName());
                },
                Executors.newFixedThreadPool(10)
        );

        try {
            responseAsync.get(); //wait for API response
        } catch (InterruptedException e) {
            System.out.println("Interrupted server response");
        } catch (ExecutionException e) {
            System.out.println("Interrupted server execution");
        }

        System.out.println("END OF ASYNCHRONOUS SERVER RESPONSE");
    }

    private static void printInfoServer(ObjectJsonMapper<Article> articleJsonMapper, HttpResponse response) {
        System.out.println(STR."Status  : \{response.statusCode()}");
        System.out.println(STR."Headers : \{response.headers()}");
        System.out.println(STR."Body    : \{response.body()}");

        String body = String.valueOf(response.body());
        Article serverArticle = articleJsonMapper.toObject(new TypeReference<>() {}, body);

        System.out.println(STR."RETRIEVED FROM SERVER : \{serverArticle}");
    }
}

Réponse du server MyHttpServer au client MyHttpClient

package fr.dyma;

import com.sun.net.httpserver.HttpServer;
import fr.dyma.model.Article;
import fr.dyma.service.ObjectHandler;
import fr.dyma.service.impl.ObjectHandlerImpl;

import java.io.IOException;
import java.net.InetSocketAddress;

public final class MyHttpServer {

    public static void main(String[] args) throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);

        Article serverArticle = new Article("Server article", "From the server", ":)", null);
        // Création d'un flux de communicaton Client/Server
        ObjectHandler<Article> articleHandler = new ObjectHandlerImpl<>(serverArticle);
        // Réponse server au requête client
        startServer(server, articleHandler);
    }

    public static <T> void startServer(HttpServer server, ObjectHandler<T> handler) throws IOException {
        server.createContext("/obj", handler);
        server.setExecutor(null); // creates a default executor
        server.start();
        System.out.println("Server started on port 8000 !");
    }
}

Classe générique de serialisation des objets java

package fr.dyma.service.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.io.IOException;

import static java.lang.StringTemplate.STR;

public final class ObjectJsonMapper<T> {
    /**
     * From Json to Object
     * @param typeReference the serialized object type reference
     * @param json the json ton convert to object
     * @return T the type of object that will be serialized
     */
    public T toObject(TypeReference<T> typeReference, String json) {
        var objectMapper = new ObjectMapper();

        // To fix jackson error unknown or transient field
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        // To fix jackson data/time error
        objectMapper.registerModule(new JavaTimeModule());

        T data = null;

        try {
            data = objectMapper.readValue(json, typeReference);

        } catch (IOException e) {
            System.out.println(STR."Error reading \{json}: \{e.getMessage()}");
        }

        return data;
    }


    /**
     * From Object to Json
     * @param obj The Object to convert to Json format
     * @return The Json representation
     * @throws JsonProcessingException
     */
    public String toJson(T obj) throws JsonProcessingException {
        var objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper.writeValueAsString(obj);
    }
}

Classe se de gestion de flux HTTP Sever / Client

package fr.dyma.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.sun.net.httpserver.HttpExchange;
import fr.dyma.service.ObjectHandler;
import fr.dyma.service.util.ObjectJsonMapper;

import java.io.IOException;

public class ObjectHandlerImpl<T> implements ObjectHandler<T> {

    private final T obj;
    public ObjectHandlerImpl(T object) {
        this.obj = object;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
        // Read client body
        var body = new String(exchange.getRequestBody().readAllBytes());
        var objectJsonMapper = new ObjectJsonMapper<T>();
        var clientObject = objectJsonMapper.toObject(new TypeReference<>() {}, body);
        System.out.println(STR."Object from client : \{clientObject}");

        // Return object to client
        String serverJson = objectJsonMapper.toJson(this.obj);
        byte[] serverJsonBytes = serverJson.getBytes();
        exchange.sendResponseHeaders(200, serverJsonBytes.length);
        var out = exchange.getResponseBody();
        out.write(serverJsonBytes);
    }
}