Skip to main content

juneau-rest-client-classic

Which client?

juneau-rest-client (org.apache.juneau.rest.client.RestClient) is the recommended REST client. It is generally available in 10.0.0, transport-agnostic, and uses the JDK HttpClient by default. juneau-rest-client-classic (org.apache.juneau.rest.client.classic.RestClient, Apache HttpClient 4.5) stays supported and is not deprecated; it is the stable choice for existing users. See Choosing a REST Client.

This page documents the classic client, which ships in the juneau-rest-client-classic module as of 10.0.0. The recommended juneau-rest-client artifact is described in Next-Generation REST Client.

Maven
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-client-classic</artifactId>
<version>${juneau.version}</version>
</dependency>
Jarjuneau-rest-client-classic-10.0.0.jar
OSGiorg.apache.juneau.rest.client.classic_10.0.0.jar
LinksMaven Central · Javadocs · Source

Contents/Features​

Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style REST calls and the ability to perform marshalling of POJOs to and from HTTP parts.

Example
// Create a basic REST client with JSON support and download a bean.
MyBean bean = RestClient.create()
.json5()
.build()
.get(URI)
.run()
.assertStatus().asCode().is(200)
.assertHeader("Content-Type").matchesSimple("application/json*")
.getContent().as(MyBean.class);

Breaking apart the fluent call, we can see the classes being used:

RestClient.Builder builder = RestClient.create().json5();
RestClient client = builder.build();
RestRequest req = client.get(URI);
RestResponse res = req.run();
RestResponseStatusLineAssertion statusLineAssertion = res.assertStatus();
FluentIntegerAssertion codeAssertion = statusLineAssertion.asCode();
res = codeAssertion.is(200);
FluentStringAssertion headerAssertion = res.assertHeader("Content-Type");
res = headerAssertion.matchesSimple("application/json*");
ResponseContent content = res.getContent();
MyBean bean = content.as(MyBean.class);

It additionally provides support for creating remote proxy interfaces using REST as the transport medium.

Example
// Define a Remote proxy for interacting with a REST interface.
@Remote(path="/petstore")
public interface PetStore {

@RemotePost("/pets")
Pet addPet(
@Content CreatePet pet,
@Header("E-Tag") UUID etag,
@Query("debug") boolean debug
);
}

PetStore store = RestClient
.create()
.json5()
.build()
.getRemote(PetStore.class, "http://localhost:10000");
CreatePet createPet = new CreatePet("Fluffy", 9.99);
Pet pet = store.addPet(createPet, UUID.randomUUID(), true);

The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality:

REST Client Class HierarchyRestClient implements HttpClient, creates RestRequest objectsRestRequest implements HttpUriRequest, creates RestResponse objectsRestResponse implements HttpResponse, creates ResponseContent and ResponseHeader objectsResponseContent implements HttpEntity

Instances of this class are built using the RestClient.Builder class which can be constructed using the RestClient.create() method as shown above.

Clients are typically created with a root URI so that relative URIs can be used when making requests.

This is done using the RestClient.Builder.rootUrl(Object) method.

Example
// Create a client where all URIs are relative to localhost.
RestClient client = RestClient.create().json().rootUrl("http://localhost:10000").build();

// Use relative paths.
String content = client.get("/subpath").run().getContent().asString();

The root URL can also be inspected after the client is built using RestClient.getRootUrl().

String rootUrl = client.getRootUrl(); // "http://localhost:10000"

Dynamic Root URL​

The RestClient.Builder.rootUrl(Supplier<String>) overload allows the root URL to be computed dynamically at request time. The supplier is called on every request, so the URL can change between requests without rebuilding the client.

Three common patterns are supported:

Swappable reference — use an AtomicReference when you need to point the client at a different host at any time:

Example
var urlRef = new AtomicReference<>("https://host1");
RestClient client = RestClient.create().json().rootUrl(urlRef::get).build();

// Later, switch to a different host without rebuilding the client.
urlRef.set("https://host2");

Refreshable cached value — use Memoizer (via Suppliers.memoizer()) when computing the URL is expensive (e.g. service discovery) and you want it cached until explicitly refreshed:

Example
// URL is fetched from service discovery once, then cached.
Memoizer<String> url = Suppliers.memoizer(() -> serviceDiscovery.findUrl("my-service"));
RestClient client = RestClient.create().json().rootUrl(url).build();

// After a failover, force re-evaluation on the next request.
url.reset();

Purely dynamic — use a plain lambda when the URL must be re-evaluated on every request:

Example
// URL is read from a live config source on every request.
RestClient client = RestClient.create().json().rootUrl(() -> config.getRootUrl()).build();

The RestClient class creates RestRequest objects using the following methods:

RestClientget(uri) / get()put(uri,body) / put(uri)post(uri,body) / post(uri)patch(uri,body) / patch(uri)delete(uri)head(uri)options(uri)formPost(uri,body) / formPost(uri)formPostPairs(uri,parameters...)request(method,uri,body)

The RestRequest class creates RestResponse objects using the following methods:

RestRequestrun()complete()

The distinction between the two methods is that complete() automatically consumes the response body and run() does not.

Note that you must consume response bodies in order for HTTP connections to be freed up for reuse!

The InputStreams returned by the ResponseContent object are auto-closing once they are exhausted, so it is often not necessary to explicitly close them.

The following examples show the distinction between the two calls:

// Consuming the response, so use run().
String content = client.get(URI).run().getContent().asString();

// Only interested in response status code, so use complete().
int status = client.get(URI).complete().getStatusCode();

Resource Management with Try-With-Resources​

Both RestRequest and RestResponse implement AutoCloseable and can be used in try-with-resources blocks for automatic resource cleanup.

Recommended Pattern

Use try-with-resources to ensure proper cleanup of HTTP connections:

// RestRequest will automatically close the response when the try block exits
try (RestRequest req = client.get(URI)) {
String content = req.run().getContent().asString();
}

// Or explicitly manage both request and response
try (RestRequest req = client.get(URI);
RestResponse res = req.run()) {
String content = res.getContent().asString();
}

The close() methods follow these rules:

  • Unchecked exceptions (RuntimeException and Error) are allowed to propagate for debuggability
  • Checked exceptions are caught and logged but not thrown, following AutoCloseable best practices

This design prevents close exceptions from masking the original exception while still making programming errors visible during development.

REST Client Stacks​

Juneau ships two REST client stacks:

  • The classic org.apache.juneau.rest.client.classic.RestClient (HC4.5-based, supports the rich fluent surface shown above) in the juneau-rest-client-classic Maven module.
  • The next-generation org.apache.juneau.rest.client.RestClient (transport-agnostic, no Apache HttpClient dependency) in the juneau-rest-client Maven module — see Next-Generation REST Client.

Both clients share the same fluent style for marshalling POJOs to and from HTTP parts.

REST Testing Framework​

The MockRestClient class is used for performing serverless unit testing of @Rest-annotated and @Remote-annotated classes. It perform full serialization and parsing of the HTTP request and responses, but bypasses the network layer to significantly improve speed while still performing real testing.

Example
public class MockTest {

// A simple bean with one field.
public static class MyBean {
public int foo = 1;
}

// Our REST resource to test.
// Simply echos the response.
@Rest
public static class EchoRest extends BasicRestServlet {

@RestPut
public MyBean echo(@Content MyBean bean) {
return bean;
}
}

// Our JUnit test.
@Test
public void testEcho() throws Exception {

var myBean = new MyBean();

// Do a round-trip on the bean through the REST interface
myBean = MockRestClient
.create(EchoRest.class)
.json5()
.build()
.put("/echo", myBean)
.run()
.assertStatus().is(200)
.assertContent().is("{foo:1}")
.getContent().as(MyBean.class);

assertEquals(1, myBean.foo);
}
}

In This Section​