Skip to main content

Jetty HTTP Client Transport

Maven Dependencies​

<!-- Juneau transport adapter -->
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-rest-client-jetty</artifactId>
<version>${juneau.version}</version>
</dependency>

<!-- Jetty 12 HTTP Client (transitive from the adapter, pinned here for clarity) -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>12.1.9</version>
</dependency>

Overview​

juneau-rest-client-jetty provides an HttpTransport implementation backed by the Jetty 12 HttpClient (org.eclipse.jetty:jetty-client). Consider it when:

  • Your application already embeds a Jetty server (e.g., via juneau-microservice-jetty) and you want to share the same Jetty lifecycle for outbound HTTP calls.
  • You want a Jetty-native HTTP client without pulling in Apache HttpComponents or OkHttp.

The request body is streamed via OutputStreamRequestContent on a background thread, so large bodies do not require full in-memory buffering. Response body bytes are made available as an InputStream via Jetty's InputStreamResponseListener.

The transport is auto-discovered via ServiceLoader when the module is on the classpath. An explicit .transport(...) call always wins over auto-discovery.


Getting Started​

Zero-configuration default​

Note

JettyHttpTransport.create() and JettyHttpTransportBuilder.build() declare throws Exception because they may need to start the underlying Jetty HttpClient.

import org.apache.juneau.marshall.json5.*;
import org.apache.juneau.rest.client.*;
import org.apache.juneau.rest.client.jetty.*;

RestClient client;
try {
client = RestClient.builder()
.transport(JettyHttpTransport.create()) // starts a new HttpClient internally
.serializer(Json5Serializer.DEFAULT)
.parser(Json5Parser.DEFAULT)
.rootUrl("https://api.example.com")
.build();
} catch (Exception e) {
throw new RuntimeException("Failed to start Jetty transport", e);
}

try (var resp = client.get("/users/123").run()) {
var user = resp.body().as(User.class);
}

client.close();

JettyHttpTransport.create() is shorthand for JettyHttpTransport.builder().build(). It creates and starts a new HttpClient with default settings and a 30-second response-header timeout.

Auto-discovery​

Add the module to the classpath and omit the .transport(...) call:

RestClient client = RestClient.builder()
.serializer(Json5Serializer.DEFAULT)
.parser(Json5Parser.DEFAULT)
.rootUrl("https://api.example.com")
.build();

Auto-discovery uses the same throws Exception build path internally; the exception is wrapped in an IllegalStateException if thrown.


Configuring the Underlying HttpClient​

Use JettyHttpTransportBuilder to supply a pre-configured HttpClient or to adjust the response-header timeout.

import org.eclipse.jetty.client.*;
import org.apache.juneau.rest.client.*;
import org.apache.juneau.rest.client.jetty.*;

// Build your own Jetty HttpClient
HttpClient jettyClient = new HttpClient();
// ... configure jettyClient ...

RestClient client;
try {
client = RestClient.builder()
.transport(JettyHttpTransport.builder()
.httpClient(jettyClient)
.responseTimeoutMs(15_000) // wait up to 15 s for response headers
.build())
.serializer(Json5Serializer.DEFAULT)
.parser(Json5Parser.DEFAULT)
.rootUrl("https://api.example.com")
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}

If the HttpClient you supply has not been started, JettyHttpTransport starts it automatically.

Response-Header Timeout​

JettyHttpTransportBuilder.responseTimeoutMs(long) sets the maximum time to wait (in milliseconds) for the HTTP status line and headers after the request has been sent. It does not limit how long the response body takes to stream.

ValueBehaviour
> 0 (default: 30_000)Wait that many milliseconds; throw TransportException on timeout.
0 or negativeWait indefinitely.
JettyHttpTransport transport = JettyHttpTransport.builder()
.responseTimeoutMs(60_000) // 60 s for slow endpoints
.build();

SSL / TLS​

Configure SSL on the HttpClient before passing it to the builder:

import org.eclipse.jetty.client.*;
import org.eclipse.jetty.util.ssl.*;

SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setKeyStorePath("/path/to/keystore.jks");
sslContextFactory.setKeyStorePassword("secret");

HttpClient tlsClient = new HttpClient(sslContextFactory);

JettyHttpTransport transport = JettyHttpTransport.builder()
.httpClient(tlsClient)
.build();

Lifecycle Management​

JettyHttpTransport.close() calls HttpClient.stop(). Map this to RestClient.close():

RestClient client = RestClient.builder()
.transport(JettyHttpTransport.builder().build())
.build();

// ... application runs ...

// At shutdown — stops the Jetty HttpClient
client.close();

When embedding inside a JettyMicroservice, the transport's lifecycle aligns naturally with the server because both use the same Jetty runtime. If you pass your own HttpClient to the adapter, stopping the client via RestClient.close() also stops that shared instance — factor this in if other components depend on it.


See Also​