vars = new LinkedHashMap<>();
+ vars.put("group", "admin");
+ vars.put("id", "7");
+ assertEquals("admin-7",
+ RestInvocationHandler.expandSegment("{group}-{id}", vars));
+ }
+
+ @Test
+ void testExpandSegmentUnknownVariable() {
+ assertEquals("{unknown}",
+ RestInvocationHandler.expandSegment("{unknown}", Collections.emptyMap()));
+ }
+
+ @Test
+ void testParamToStringBasicTypes() {
+ assertEquals("42", RestInvocationHandler.paramToString(42));
+ assertEquals("true", RestInvocationHandler.paramToString(true));
+ assertEquals("hello", RestInvocationHandler.paramToString("hello"));
+ }
+
+ enum Color { RED, GREEN, BLUE }
+
+ enum OverriddenToString {
+ ALPHA;
+
+ @Override
+ public String toString() {
+ return "custom-alpha";
+ }
+ }
+
+ @Test
+ void testParamToStringEnumUsesName() {
+ assertEquals("RED", RestInvocationHandler.paramToString(Color.RED));
+ assertEquals("GREEN", RestInvocationHandler.paramToString(Color.GREEN));
+ }
+
+ @Test
+ void testParamToStringEnumIgnoresOverriddenToString() {
+ assertEquals("ALPHA", RestInvocationHandler.paramToString(OverriddenToString.ALPHA));
+ }
+
+}
diff --git a/httpclient5-jakarta-rest-client/src/test/java/org/apache/hc/client5/http/rest/examples/RestClientMain.java b/httpclient5-jakarta-rest-client/src/test/java/org/apache/hc/client5/http/rest/examples/RestClientMain.java
new file mode 100644
index 0000000000..0af55823aa
--- /dev/null
+++ b/httpclient5-jakarta-rest-client/src/test/java/org/apache/hc/client5/http/rest/examples/RestClientMain.java
@@ -0,0 +1,158 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.hc.client5.http.rest.examples;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.rest.RestClientBuilder;
+
+/**
+ * Example demonstrating the Jakarta REST client module backed by the
+ * Apache HttpComponents async client.
+ *
+ * The example uses httpbin.org as the target endpoint and shows query
+ * parameter binding and JSON request / response body handling.
+ *
+ * @since 5.7
+ */
+public final class RestClientMain {
+
+ private RestClientMain() {
+ }
+
+ public static void main(final String[] args) throws Exception {
+ try (final CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault()) {
+ httpClient.start();
+
+ final HttpBinApi api = RestClientBuilder.newBuilder()
+ .baseUri("https://httpbin.org")
+ .httpClient(httpClient)
+ .build(HttpBinApi.class);
+
+ final GetResponse getResponse = api.get("en");
+ System.out.println("GET:");
+ System.out.println(" url = " + getResponse.url);
+ System.out.println(" args.lang = " + value(getResponse.args, "lang"));
+
+ final Widget widget = new Widget(1, "test");
+ final PostResponse postResponse = api.post(widget);
+
+ System.out.println("POST:");
+ System.out.println(" url = " + postResponse.url);
+ System.out.println(" echoed json = " + postResponse.json);
+ }
+ }
+
+ private static String value(final Map map, final String key) {
+ return map != null ? map.get(key) : null;
+ }
+
+ @Path("/")
+ interface HttpBinApi {
+
+ @GET
+ @Path("get")
+ @Produces("application/json")
+ GetResponse get(@QueryParam("lang") String lang);
+
+ @POST
+ @Path("post")
+ @Consumes("application/json")
+ @Produces("application/json")
+ PostResponse post(Widget body);
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static final class GetResponse {
+
+ public Map args;
+ public Map headers;
+ public String origin;
+ public String url;
+
+ @Override
+ public String toString() {
+ return "GetResponse{" +
+ "args=" + args +
+ ", headers=" + headers +
+ ", origin='" + origin + '\'' +
+ ", url='" + url + '\'' +
+ '}';
+ }
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ static final class PostResponse {
+
+ public String data;
+ public Map headers;
+ public Widget json;
+ public String url;
+
+ @Override
+ public String toString() {
+ return "PostResponse{" +
+ "data='" + data + '\'' +
+ ", headers=" + headers +
+ ", json=" + json +
+ ", url='" + url + '\'' +
+ '}';
+ }
+ }
+
+ static final class Widget {
+
+ public int id;
+ public String name;
+
+ public Widget() {
+ }
+
+ public Widget(final int id, final String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Widget{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ '}';
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ade28c3889..2d2a6cef6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,8 @@
1.59.0
1.26.2
2.9.3
+ 3.1.0
+ 2.21.1
@@ -116,6 +118,11 @@
httpcore5-reactive
${httpcore.version}
+
+ org.apache.httpcomponents.core5
+ httpcore5-jackson2
+ ${httpcore.version}
+
org.apache.httpcomponents.client5
httpclient5
@@ -266,6 +273,16 @@
caffeine
${caffeine.version}
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ ${jakarta.ws.rs.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
@@ -275,6 +292,7 @@
httpclient5-observation
httpclient5-fluent
httpclient5-cache
+ httpclient5-jakarta-rest-client
httpclient5-testing