View Javadoc
1   package fr.tiogars.chat.client;
2   
3   import java.io.IOException;
4   import java.net.URI;
5   import java.net.http.HttpClient;
6   import java.net.http.HttpRequest;
7   import java.net.http.HttpResponse;
8   import java.util.List;
9   import com.fasterxml.jackson.databind.ObjectMapper;
10  import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
11  
12  /** Shared HTTP transport for reusable taxonomy CRUD clients. */
13  public abstract class TaxonomyClient<T, C, U> {
14      private final URI baseUri;
15      private final String path;
16      private final Class<T> type;
17      private final HttpClient httpClient;
18      private final ObjectMapper mapper;
19  
20      protected TaxonomyClient(URI baseUri, String path, Class<T> type) {
21          this(baseUri, path, type, HttpClient.newHttpClient(), new ObjectMapper().registerModule(new JavaTimeModule())
22                  .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
23      }
24  
25      protected TaxonomyClient(URI baseUri, String path, Class<T> type, HttpClient httpClient, ObjectMapper mapper) {
26          this.baseUri = baseUri;
27          this.path = path;
28          this.type = type;
29          this.httpClient = httpClient;
30          this.mapper = mapper;
31      }
32  
33      public T create(C request) throws IOException, InterruptedException {
34          return mapper.readValue(send("", "POST", request, 201), type);
35      }
36  
37      public T update(long id, U request) throws IOException, InterruptedException {
38          return mapper.readValue(send("/" + id, "PUT", request, 200), type);
39      }
40  
41      public T findById(long id) throws IOException, InterruptedException {
42          return mapper.readValue(send("/" + id, "GET", null, 200), type);
43      }
44  
45      public List<T> findAll() throws IOException, InterruptedException {
46          return mapper.readValue(send("", "GET", null, 200), mapper.getTypeFactory().constructCollectionType(List.class, type));
47      }
48  
49      public void delete(long id) throws IOException, InterruptedException {
50          send("/" + id, "DELETE", null, 204);
51      }
52  
53      private String send(String suffix, String method, Object body, int expectedStatus) throws IOException, InterruptedException {
54          var publisher = body == null ? HttpRequest.BodyPublishers.noBody()
55                  : HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(body));
56          var request = HttpRequest.newBuilder(baseUri.resolve(path + suffix)).header("Accept", "application/json")
57                  .header("Content-Type", "application/json").method(method, publisher).build();
58          var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
59          if (response.statusCode() != expectedStatus) {
60              throw new TaskException("Expected HTTP " + expectedStatus + " but received " + response.statusCode());
61          }
62          return response.body();
63      }
64  }