This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 6613eee170 TODO-242: Add petstore @Remote typed-client demo +
Config/SVL showcase.
6613eee170 is described below
commit 6613eee170305dc39706c77422fa583ae15b8a56
Author: James Bognar <[email protected]>
AuthorDate: Wed Jul 15 12:14:51 2026 -0400
TODO-242: Add petstore @Remote typed-client demo + Config/SVL showcase.
Phase 9 - PetStoreClient @Remote interface (juneau-petstore-core) mirroring
PetStoreResource's pet CRUD surface, plus a client-consuming Jetty container
test driving a genuine HTTP round-trip via
RestClient.remote(PetStoreClient).
Phase 10 - [Petstore] cfg section + $C{} SVL substitution in the root
router's @HtmlDocConfig header.
Also resolves 3 Eclipse warnings surfaced along the way: a
resource-suppression
idiom on the client test's try-with-resources, {@code} demotion of an
off-classpath javadoc @link, and an unused import in PetHtmlResource.
Co-authored-by: Cursor <[email protected]>
---
.../juneau/petstore/client/PetStoreClient.java | 67 ++++++++++++++++
.../juneau/petstore/client/package-info.java | 25 ++++++
.../juneau/petstore/rest/PetHtmlResource.java | 1 -
.../juneau-petstore-jetty.cfg | 15 ++++
juneau-petstore/juneau-petstore-jetty/pom.xml | 7 ++
.../juneau/petstore/jetty/RootResources.java | 10 +++
.../juneau/petstore/jetty/PetStoreClient_Test.java | 92 ++++++++++++++++++++++
.../juneau/petstore/jetty/PetstoreJetty_Test.java | 12 +++
8 files changed, 228 insertions(+), 1 deletion(-)
diff --git
a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/PetStoreClient.java
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/PetStoreClient.java
new file mode 100644
index 0000000000..b0433f1141
--- /dev/null
+++
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/PetStoreClient.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package org.apache.juneau.petstore.client;
+
+import java.util.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.remote.*;
+import org.apache.juneau.petstore.dto.*;
+
+/**
+ * Typed client view of the petstore pet CRUD surface, consumed via {@code
org.apache.juneau.rest.client.RestClient#remote(Class)}.
+ *
+ * <p>
+ * Mirrors the pet endpoints of {@link
org.apache.juneau.petstore.rest.PetStoreResource} (mounted at
+ * {@code /petstore} in both the Jetty and Spring Boot deployments).
Demonstrates Juneau's REST client consuming
+ * a REST resource written with Juneau's REST server — the client interface
and the server resource live in
+ * different modules but share nothing but the wire protocol and the {@link
Pet} DTO.
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/RestProxies">REST Proxy Basics</a>
+ * <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
+ * </ul>
+ */
+@Remote(path="/petstore")
+public interface PetStoreClient {
+
+ /**
+ * Lists all pets.
+ *
+ * @return All pets.
+ */
+ @RemoteGet("/pets")
+ List<Pet> getPets();
+
+ /**
+ * Retrieves a pet by ID.
+ *
+ * @param id The pet ID.
+ * @return The pet.
+ */
+ @RemoteGet("/pets/{id}")
+ Pet getPet(@Path("id") long id);
+
+ /**
+ * Creates a new pet.
+ *
+ * @param pet The pet to create.
+ * @return The created pet (with assigned ID).
+ */
+ @RemotePost("/pets")
+ Pet addPet(@Content Pet pet);
+}
diff --git
a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/package-info.java
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/package-info.java
new file mode 100644
index 0000000000..5ba0e81796
--- /dev/null
+++
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/client/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+/**
+ * Typed {@code @Remote} client view of the petstore API.
+ *
+ * <p>
+ * Deployment-agnostic (like {@code org.apache.juneau.petstore.rest}) so it
can be consumed by tests in either the
+ * {@code juneau-petstore-jetty} or {@code juneau-petstore-springboot}
deployment modules via
+ * {@link org.apache.juneau.rest.client.RestClient#remote(Class)}.
+ */
+package org.apache.juneau.petstore.client;
diff --git
a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
index 6a0971af32..1030a3f56b 100644
---
a/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
+++
b/juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/rest/PetHtmlResource.java
@@ -23,7 +23,6 @@ import java.util.*;
import org.apache.juneau.bean.html5.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.response.*;
-import org.apache.juneau.petstore.dto.*;
import org.apache.juneau.petstore.service.*;
import org.apache.juneau.rest.server.*;
import org.apache.juneau.rest.server.beans.*;
diff --git a/juneau-petstore/juneau-petstore-jetty/juneau-petstore-jetty.cfg
b/juneau-petstore/juneau-petstore-jetty/juneau-petstore-jetty.cfg
index d09858b04a..31fc1518d2 100644
--- a/juneau-petstore/juneau-petstore-jetty/juneau-petstore-jetty.cfg
+++ b/juneau-petstore/juneau-petstore-jetty/juneau-petstore-jetty.cfg
@@ -118,3 +118,18 @@ RestContext.disableClasspathResourceCaching.b = true
#=======================================================================================================================
[Source]
gitHub =
https://github.com/apache/juneau/blob/master/juneau-petstore/juneau-petstore-jetty/src/main/java
+
+
+#=======================================================================================================================
+# Petstore application settings
+#
+# Demonstrates the Config API + SVL variable resolver: values here are
referenced from RootResources via
+# $C{Petstore/...} substitutions in @HtmlDocConfig (e.g. the root page header).
+#=======================================================================================================================
+[Petstore]
+
+# Displayed in the root router page header via $C{Petstore/appName}.
+appName = Apache Juneau Petstore
+
+# Chained SVL example: an environment variable read through the Config API,
with a fallback default.
+buildUser = $E{USER,unknown}
diff --git a/juneau-petstore/juneau-petstore-jetty/pom.xml
b/juneau-petstore/juneau-petstore-jetty/pom.xml
index 74ddc0c2e8..295d4146c4 100644
--- a/juneau-petstore/juneau-petstore-jetty/pom.xml
+++ b/juneau-petstore/juneau-petstore-jetty/pom.xml
@@ -84,6 +84,13 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
+ <!-- Next-gen REST client, used by the @Remote typed-client
(PetStoreClient) round-trip test. -->
+ <dependency>
+ <groupId>org.apache.juneau</groupId>
+ <artifactId>juneau-rest-client</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
b/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
index 9993ba71de..d6c630aaa8 100644
---
a/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
+++
b/juneau-petstore/juneau-petstore-jetty/src/main/java/org/apache/juneau/petstore/jetty/RootResources.java
@@ -33,6 +33,13 @@ import org.apache.juneau.rest.server.widget.*;
* {@link ShutdownResource}). The admin trio is a documented Jetty-only
non-parity feature — the Spring Boot
* deployment in {@code juneau-petstore-springboot} does not mount it.
*
+ * <p>
+ * Also showcases the Config API + SVL variable resolver: the root page's
header is driven by
+ * {@code $C{Petstore/appName}}, which resolves against the {@code [Petstore]}
section of
+ * {@code juneau-petstore-jetty.cfg} (the same {@code $C{...}} pattern already
used by the {@code source} navlink
+ * below, which resolves {@code $C{Source/gitHub}}). Jetty-only — Spring
Boot's embedded container has no
+ * {@code .cfg}/{@code Config} equivalent.
+ *
* <h5 class='section'>See Also:</h5><ul>
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/JuneauPetstore">juneau-petstore</a>
* </ul>
@@ -59,6 +66,9 @@ import org.apache.juneau.rest.server.widget.*;
widgets={
ContentTypeMenuItem.class
},
+ header={
+ "<h3>$C{Petstore/appName}</h3>"
+ },
navlinks={
"api: servlet:/api",
"stats: servlet:/stats",
diff --git
a/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetStoreClient_Test.java
b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetStoreClient_Test.java
new file mode 100644
index 0000000000..d1d0adf257
--- /dev/null
+++
b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetStoreClient_Test.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package org.apache.juneau.petstore.jetty;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.marshall.json.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.microservice.*;
+import org.apache.juneau.petstore.client.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.rest.client.*;
+import org.apache.juneau.testing.*;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.*;
+
+/**
+ * Integration test that drives the petstore's own REST API through the typed
{@link PetStoreClient}
+ * {@code @Remote} interface, over a real HTTP round-trip against a live
Jetty-hosted server.
+ *
+ * <p>
+ * Complements {@link PetstoreJetty_Test} (which exercises the API via a plain
{@code java.net.http.HttpClient}) by
+ * proving that Juneau's own next-generation {@link RestClient} can consume a
Juneau REST resource end-to-end via
+ * {@link RestClient#remote(Class)} — no hand-written request/response wiring,
just the typed interface.
+ */
+@JettyMicroserviceTest
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@SuppressWarnings({
+ "resource" // newClient() returns a Closeable owned by the caller
(closed via try-with-resources in each test); Eclipse JDT @Owning warning is by
design.
+})
+class PetStoreClient_Test {
+
+ @RegisterExtension
+ static MicroserviceTestFixture fixture =
MicroserviceTestFixture.create()
+ .configurations(App.AppConfig.class);
+
+ /**
+ * Builds a JSON-speaking client rooted at the fixture's live server.
+ *
+ * <p>
+ * The next-gen {@link RestClient} has no implicit JSON fallback
(10.0.0 removed it) — the parser set and
+ * default serializer must be configured explicitly.
+ */
+ private static RestClient newClient() {
+ return RestClient.builder()
+ .rootUrl(fixture.getRootUrl().toString())
+
.parsers(ParserSet.create().add(JsonParser.DEFAULT).build())
+ .defaultSerializer(JsonSerializer.DEFAULT)
+ .build();
+ }
+
+ @Test void a01_typedClientListsSeededPets() throws Exception {
+ try (var client = newClient()) {
+ var svc = client.remote(PetStoreClient.class);
+ var pets = svc.getPets();
+ assertEquals(9, pets.size());
+ }
+ }
+
+ @Test void a02_typedClientGetsSeededPetById() throws Exception {
+ try (var client = newClient()) {
+ var svc = client.remote(PetStoreClient.class);
+ var pet = svc.getPet(1L);
+ assertEquals("Mr. Frisky", pet.getName());
+ }
+ }
+
+ @Test void a03_typedClientAddsPet_thenVisibleInList() throws Exception {
+ try (var client = newClient()) {
+ var svc = client.remote(PetStoreClient.class);
+ var created = svc.addPet(new
Pet().setSpecies(Species.DOG).setName("Fido").setPrice(25.0f).setStatus(PetStatus.AVAILABLE));
+ assertNotEquals(0L, created.getId());
+
+ var fetched = svc.getPet(created.getId());
+ assertEquals("Fido", fetched.getName());
+ }
+ }
+}
diff --git
a/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
index 99472a6c57..5a7fac9a5a 100644
---
a/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
+++
b/juneau-petstore/juneau-petstore-jetty/src/test/java/org/apache/juneau/petstore/jetty/PetstoreJetty_Test.java
@@ -137,6 +137,18 @@ class PetstoreJetty_Test extends TestBase {
assertTrue(body.contains("petstore"), "expected petstore link
on root: " + body);
}
+ /**
+ * Confirms the Config API + SVL var-resolver showcase: {@code
RootResources}'s {@code @HtmlDocConfig(header=...)}
+ * references {@code $C{Petstore/appName}}, which resolves against the
{@code [Petstore]} section of
+ * {@code juneau-petstore-jetty.cfg} — proving the {@code .cfg} value
reaches the rendered page.
+ */
+ @Test void a02_rootHeaderResolvesConfigSvlAppName() throws Exception {
+ var resp = get("/", "text/html");
+ assertEquals(200, resp.statusCode(), "body: " + resp.body());
+ var body = resp.body();
+ assertTrue(body.contains("Apache Juneau Petstore"), "expected
$C{Petstore/appName}-resolved header: " + body);
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// b — petstore CRUD over real HTTP, JSON
//-----------------------------------------------------------------------------------------------------------------