diff --git a/http-client/pom.xml b/http-client/pom.xml index 24f3f9b5..971f7936 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -66,7 +66,7 @@ io.avaje avaje-http-api - 2.9-SNAPSHOT + ${project.version} test diff --git a/http-client/src/main/java/io/avaje/http/client/DHttpClientContext.java b/http-client/src/main/java/io/avaje/http/client/DHttpClientContext.java index 1d293428..987b2a43 100644 --- a/http-client/src/main/java/io/avaje/http/client/DHttpClientContext.java +++ b/http-client/src/main/java/io/avaje/http/client/DHttpClientContext.java @@ -1,5 +1,7 @@ package io.avaje.http.client; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Type; import java.net.http.HttpHeaders; import java.net.http.HttpRequest; @@ -9,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; @@ -340,4 +343,17 @@ private String authToken() { String maxResponseBody(String body) { return body.length() > 1_000 ? body.substring(0, 1_000) + " ..." : body; } + + @Override + public void close() { + if (Integer.getInteger("java.specification.version") >= 21) { + try { + MethodHandles.lookup() + .findVirtual(java.net.http.HttpClient.class, "close", MethodType.methodType(void.class)) + .invokeExact(httpClient); + } catch (Throwable t) { + throw new IllegalStateException("Failed to close java.net.http.HttpClient instance"); + } + } + } } diff --git a/http-client/src/main/java/io/avaje/http/client/HttpClient.java b/http-client/src/main/java/io/avaje/http/client/HttpClient.java index 38dc149b..73cd7044 100644 --- a/http-client/src/main/java/io/avaje/http/client/HttpClient.java +++ b/http-client/src/main/java/io/avaje/http/client/HttpClient.java @@ -3,7 +3,6 @@ import java.net.Authenticator; import java.net.CookieHandler; import java.net.ProxySelector; -import java.net.http.HttpRequest; import java.time.Duration; import java.util.Map; import java.util.concurrent.Executor; @@ -33,7 +32,7 @@ * * } */ -public interface HttpClient { +public interface HttpClient extends AutoCloseable { /** * Return the builder to config and build the client context. @@ -93,6 +92,19 @@ static Builder builder() { */ HttpClient.Metrics metrics(boolean reset); + /** + * Note: invoking this method has no effect on JDK versions less than 21. + * + *

Initiates an orderly shutdown in which http requests previously submitted are run to + * completion, but no new requests will be accepted. Running a request to completion may involve + * running several operations in the background, including waiting for responses to be delivered. + * This method waits until all operations have completed execution and the client has terminated. + * + * @see {@linkplain java.net.http.HttpClient#close} + */ + @Override + void close(); + /** * Builds the HttpClient. * diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java index 52d7d944..1291a06e 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java @@ -1,5 +1,6 @@ package io.avaje.http.generator.client; +import io.avaje.http.generator.core.APContext; import io.avaje.http.generator.core.BaseControllerWriter; import io.avaje.http.generator.core.ControllerReader; import io.avaje.http.generator.core.MethodReader; @@ -62,12 +63,17 @@ private void writeMethods() { for (final ClientMethodWriter methodWriter : methodList) { methodWriter.write(); } + writer.append(" @Override").eol(); + writer.append(" public void close() {").eol(); + writer.append(" this.client.close();").eol(); + writer.append(" }").eol(); } private void writeClassStart() { writer.append(AT_GENERATED).eol(); AnnotationUtil.writeAnnotations(writer, reader.beanType()); - writer.append("public class %s%s implements %s {", shortName, suffix, shortName).eol().eol(); + + writer.append("public class %s%s implements %s, AutoCloseable {", shortName, suffix, shortName).eol().eol(); writer.append(" private final HttpClient client;").eol().eol();