Java API for RESTful Web Services
—
The JAX-RS Client API provides a fluent interface for consuming RESTful web services. It supports synchronous, asynchronous, and reactive invocation patterns with built-in support for request/response filtering, configuration, and entity processing.
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.AsyncInvoker;
import javax.ws.rs.client.SyncInvoker;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.client.RxInvoker;
import javax.ws.rs.client.CompletionStageRxInvoker;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.WriterInterceptor;
import java.util.concurrent.Future;
import java.util.concurrent.CompletionStage;
import java.io.IOException;Main entry point for creating Client instances.
public abstract class ClientBuilder implements Configurable<ClientBuilder> {
public static ClientBuilder newBuilder();
public static Client newClient();
public static Client newClient(Configuration configuration);
public abstract ClientBuilder withConfig(Configuration config);
public abstract Client build();
}Main client interface for building requests.
public interface Client extends Configurable<Client> {
void close();
WebTarget target(String uri);
WebTarget target(URI uri);
WebTarget target(UriBuilder uriBuilder);
WebTarget target(Link link);
Invocation.Builder invocation(Link link);
}Basic Client Usage:
// Create client
Client client = ClientBuilder.newClient();
// Simple GET request
Response response = client.target("https://api.example.com/users/123")
.request(MediaType.APPLICATION_JSON)
.get();
if (response.getStatus() == 200) {
User user = response.readEntity(User.class);
System.out.println("User: " + user.getName());
}
// Always close the client
client.close();Represents a resource target identified by URI.
public interface WebTarget extends Configurable<WebTarget> {
URI getUri();
UriBuilder getUriBuilder();
WebTarget path(String path);
WebTarget resolveTemplate(String name, Object value);
WebTarget resolveTemplate(String name, Object value, boolean encodeSlashInPath);
WebTarget resolveTemplateFromEncoded(String name, Object value);
WebTarget resolveTemplates(Map<String, Object> templateValues);
WebTarget resolveTemplates(Map<String, Object> templateValues, boolean encodeSlashInPath);
WebTarget resolveTemplatesFromEncoded(Map<String, Object> templateValues);
WebTarget matrixParam(String name, Object... values);
WebTarget queryParam(String name, Object... values);
Invocation.Builder request();
Invocation.Builder request(String... acceptedResponseTypes);
Invocation.Builder request(MediaType... acceptedResponseTypes);
}WebTarget Usage Examples:
Client client = ClientBuilder.newClient();
// Building complex URIs
WebTarget baseTarget = client.target("https://api.example.com");
// Path building
WebTarget usersTarget = baseTarget.path("/users").path("/{userId}");
// Template resolution
WebTarget specificUser = usersTarget.resolveTemplate("userId", "123");
// Query parameters
WebTarget searchTarget = baseTarget.path("/search")
.queryParam("q", "java")
.queryParam("limit", 10)
.queryParam("offset", 0);
// Matrix parameters
WebTarget productsTarget = baseTarget.path("/products")
.matrixParam("color", "red")
.matrixParam("size", "large");
// Execute requests
Response userResponse = specificUser.request(MediaType.APPLICATION_JSON).get();
Response searchResponse = searchTarget.request(MediaType.APPLICATION_JSON).get();public interface Invocation {
Invocation property(String name, Object value);
Response invoke();
<T> T invoke(Class<T> responseType);
<T> T invoke(GenericType<T> responseType);
Future<Response> submit();
<T> Future<T> submit(Class<T> responseType);
<T> Future<T> submit(GenericType<T> responseType);
<T> Future<T> submit(InvocationCallback<T> callback);
}
public static interface Invocation.Builder extends SyncInvoker {
Invocation build(String method);
Invocation build(String method, Entity<?> entity);
Invocation buildGet();
Invocation buildDelete();
Invocation buildPost(Entity<?> entity);
Invocation buildPut(Entity<?> entity);
AsyncInvoker async();
Invocation.Builder accept(String... mediaTypes);
Invocation.Builder accept(MediaType... mediaTypes);
Invocation.Builder acceptLanguage(Locale... locales);
Invocation.Builder acceptLanguage(String... locales);
Invocation.Builder acceptEncoding(String... encodings);
Invocation.Builder cookie(Cookie cookie);
Invocation.Builder cookie(String name, String value);
Invocation.Builder cacheControl(CacheControl cacheControl);
Invocation.Builder header(String name, Object value);
Invocation.Builder headers(MultivaluedMap<String, Object> headers);
Invocation.Builder property(String name, Object value);
CompletionStageRxInvoker rx();
<T extends RxInvoker> T rx(Class<T> clazz);
}public interface SyncInvoker {
Response get();
<T> T get(Class<T> responseType);
<T> T get(GenericType<T> responseType);
Response put(Entity<?> entity);
<T> T put(Entity<?> entity, Class<T> responseType);
<T> T put(Entity<?> entity, GenericType<T> responseType);
Response post(Entity<?> entity);
<T> T post(Entity<?> entity, Class<T> responseType);
<T> T post(Entity<?> entity, GenericType<T> responseType);
Response delete();
<T> T delete(Class<T> responseType);
<T> T delete(GenericType<T> responseType);
Response head();
Response options();
<T> T options(Class<T> responseType);
<T> T options(GenericType<T> responseType);
Response trace();
<T> T trace(Class<T> responseType);
<T> T trace(GenericType<T> responseType);
Response method(String name);
<T> T method(String name, Class<T> responseType);
<T> T method(String name, GenericType<T> responseType);
Response method(String name, Entity<?> entity);
<T> T method(String name, Entity<?> entity, Class<T> responseType);
<T> T method(String name, Entity<?> entity, GenericType<T> responseType);
}Synchronous Usage Examples:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("https://api.example.com/users");
// GET requests
User user = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.get(User.class);
// GET with response inspection
Response response = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.get();
if (response.getStatus() == 200) {
User user = response.readEntity(User.class);
}
// POST requests
User newUser = new User("John", "john@example.com");
User created = target.request(MediaType.APPLICATION_JSON)
.post(Entity.json(newUser), User.class);
// PUT requests
User updatedUser = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.put(Entity.json(user), User.class);
// DELETE requests
Response deleteResponse = target.path("/123")
.request()
.delete();
// Custom headers
User userWithAuth = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + token)
.header("X-Client-Version", "1.0")
.get(User.class);
// Generic types for collections
List<User> users = target.request(MediaType.APPLICATION_JSON)
.get(new GenericType<List<User>>(){});public interface AsyncInvoker {
Future<Response> get();
<T> Future<T> get(Class<T> responseType);
<T> Future<T> get(GenericType<T> responseType);
<T> Future<T> get(InvocationCallback<T> callback);
Future<Response> put(Entity<?> entity);
<T> Future<T> put(Entity<?> entity, Class<T> responseType);
<T> Future<T> put(Entity<?> entity, GenericType<T> responseType);
<T> Future<T> put(Entity<?> entity, InvocationCallback<T> callback);
Future<Response> post(Entity<?> entity);
<T> Future<T> post(Entity<?> entity, Class<T> responseType);
<T> Future<T> post(Entity<?> entity, GenericType<T> responseType);
<T> Future<T> post(Entity<?> entity, InvocationCallback<T> callback);
Future<Response> delete();
<T> Future<T> delete(Class<T> responseType);
<T> Future<T> delete(GenericType<T> responseType);
<T> Future<T> delete(InvocationCallback<T> callback);
Future<Response> head();
Future<Response> head(InvocationCallback<Response> callback);
Future<Response> options();
<T> Future<T> options(Class<T> responseType);
<T> Future<T> options(GenericType<T> responseType);
<T> Future<T> options(InvocationCallback<T> callback);
Future<Response> trace();
<T> Future<T> trace(Class<T> responseType);
<T> Future<T> trace(GenericType<T> responseType);
<T> Future<T> trace(InvocationCallback<T> callback);
Future<Response> method(String name);
<T> Future<T> method(String name, Class<T> responseType);
<T> Future<T> method(String name, GenericType<T> responseType);
<T> Future<T> method(String name, InvocationCallback<T> callback);
Future<Response> method(String name, Entity<?> entity);
<T> Future<T> method(String name, Entity<?> entity, Class<T> responseType);
<T> Future<T> method(String name, Entity<?> entity, GenericType<T> responseType);
<T> Future<T> method(String name, Entity<?> entity, InvocationCallback<T> callback);
}public interface InvocationCallback<RESPONSE> {
void completed(RESPONSE response);
void failed(Throwable throwable);
}Asynchronous Usage Examples:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("https://api.example.com/users");
// Async with Future
Future<User> futureUser = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.async()
.get(User.class);
// Wait for result
User user = futureUser.get(); // Blocks until completion
// Async with callback
target.path("/123")
.request(MediaType.APPLICATION_JSON)
.async()
.get(new InvocationCallback<User>() {
@Override
public void completed(User user) {
System.out.println("Received user: " + user.getName());
}
@Override
public void failed(Throwable throwable) {
System.err.println("Request failed: " + throwable.getMessage());
}
});
// Async POST with callback
User newUser = new User("Jane", "jane@example.com");
target.request(MediaType.APPLICATION_JSON)
.async()
.post(Entity.json(newUser), new InvocationCallback<User>() {
@Override
public void completed(User createdUser) {
System.out.println("User created: " + createdUser.getId());
}
@Override
public void failed(Throwable throwable) {
System.err.println("User creation failed: " + throwable.getMessage());
}
});public interface RxInvoker<T> {
T get();
<R> T get(Class<R> responseType);
<R> T get(GenericType<R> responseType);
T put(Entity<?> entity);
<R> T put(Entity<?> entity, Class<R> responseType);
<R> T put(Entity<?> entity, GenericType<R> responseType);
T post(Entity<?> entity);
<R> T post(Entity<?> entity, Class<R> responseType);
<R> T post(Entity<?> entity, GenericType<R> responseType);
T delete();
<R> T delete(Class<R> responseType);
<R> T delete(GenericType<R> responseType);
T head();
T options();
<R> T options(Class<R> responseType);
<R> T options(GenericType<R> responseType);
T trace();
<R> T trace(Class<R> responseType);
<R> T trace(GenericType<R> responseType);
T method(String name);
<R> T method(String name, Class<R> responseType);
<R> T method(String name, GenericType<R> responseType);
T method(String name, Entity<?> entity);
<R> T method(String name, Entity<?> entity, Class<R> responseType);
<R> T method(String name, Entity<?> entity, GenericType<R> responseType);
}
public interface CompletionStageRxInvoker extends RxInvoker<CompletionStage> {
}Reactive Usage Examples:
import java.util.concurrent.CompletionStage;
Client client = ClientBuilder.newClient();
WebTarget target = client.target("https://api.example.com/users");
// CompletionStage-based reactive calls
CompletionStage<User> userStage = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.rx()
.get(User.class);
// Chain operations
userStage.thenCompose(user -> {
// Load user's orders
return target.path("/" + user.getId() + "/orders")
.request(MediaType.APPLICATION_JSON)
.rx()
.get(new GenericType<List<Order>>(){});
}).thenAccept(orders -> {
System.out.println("User has " + orders.size() + " orders");
}).exceptionally(throwable -> {
System.err.println("Error: " + throwable.getMessage());
return null;
});
// Parallel requests
CompletionStage<User> userStage = target.path("/123")
.request(MediaType.APPLICATION_JSON)
.rx()
.get(User.class);
CompletionStage<List<Order>> ordersStage = target.path("/123/orders")
.request(MediaType.APPLICATION_JSON)
.rx()
.get(new GenericType<List<Order>>(){});
// Combine results
userStage.thenCombine(ordersStage, (user, orders) -> {
return new UserWithOrders(user, orders);
}).thenAccept(userWithOrders -> {
System.out.println("Complete user data loaded");
});Encapsulates message entity with variant information.
public final class Entity<T> {
public static <T> Entity<T> entity(T entity, MediaType mediaType);
public static <T> Entity<T> entity(T entity, String mediaType);
public static <T> Entity<T> entity(T entity, Variant variant);
public static <T> Entity<T> json(T entity);
public static <T> Entity<T> xml(T entity);
public static <T> Entity<T> html(T entity);
public static <T> Entity<T> text(T entity);
public static <T> Entity<T> xhtml(T entity);
public static Entity<Form> form(Form form);
public static Entity<Form> form(MultivaluedMap<String, String> formData);
public Variant getVariant();
public MediaType getMediaType();
public String getEncoding();
public Locale getLanguage();
public T getEntity();
public Annotation[] getAnnotations();
}Entity Usage Examples:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("https://api.example.com/users");
// JSON entity
User user = new User("John", "john@example.com");
Response response = target.request(MediaType.APPLICATION_JSON)
.post(Entity.json(user));
// XML entity
User xmlUser = target.request(MediaType.APPLICATION_XML)
.post(Entity.xml(user), User.class);
// Custom media type
User customUser = target.request("application/vnd.api+json")
.post(Entity.entity(user, "application/vnd.api+json"), User.class);
// Form data
MultivaluedMap<String, String> formData = new MultivaluedHashMap<>();
formData.add("name", "John");
formData.add("email", "john@example.com");
Response formResponse = target.request()
.post(Entity.form(formData));
// Form object
Form form = new Form();
form.param("name", "John");
form.param("email", "john@example.com");
Response formObjectResponse = target.request()
.post(Entity.form(form));JAX-RS clients support configuration and filtering for cross-cutting concerns.
Configuration Example:
// Custom client configuration
Client client = ClientBuilder.newBuilder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.register(new LoggingFilter())
.register(new AuthenticationFilter())
.build();
// Per-request configuration
Response response = client.target("https://api.example.com/users")
.property("custom.timeout", 10000)
.request(MediaType.APPLICATION_JSON)
.header("X-API-Key", apiKey)
.get();Interface for filtering client requests before they are sent to the server.
@FunctionalInterface
public interface ClientRequestFilter {
void filter(ClientRequestContext requestContext) throws IOException;
}Interface for filtering client responses after they are received from the server.
@FunctionalInterface
public interface ClientResponseFilter {
void filter(ClientRequestContext requestContext,
ClientResponseContext responseContext) throws IOException;
}Provides access to request context during client-side filtering.
public interface ClientRequestContext {
Object getProperty(String name);
Collection<String> getPropertyNames();
void setProperty(String name, Object object);
void removeProperty(String name);
URI getUri();
void setUri(URI uri);
String getMethod();
void setMethod(String method);
MultivaluedMap<String, Object> getHeaders();
MultivaluedMap<String, String> getStringHeaders();
String getHeaderString(String name);
Date getDate();
Locale getLanguage();
MediaType getMediaType();
List<MediaType> getAcceptableMediaTypes();
List<Locale> getAcceptableLanguages();
Map<String, Cookie> getCookies();
boolean hasEntity();
Object getEntity();
void setEntity(Object entity);
void setEntity(Object entity, Annotation[] annotations, MediaType mediaType);
Class<?> getEntityClass();
Type getEntityType();
Annotation[] getEntityAnnotations();
OutputStream getEntityStream();
void setEntityStream(OutputStream outputStream);
Client getClient();
Configuration getConfiguration();
void abortWith(Response response);
}Provides access to response context during client-side filtering.
public interface ClientResponseContext {
int getStatus();
void setStatus(int code);
Response.StatusType getStatusInfo();
void setStatusInfo(Response.StatusType statusInfo);
MultivaluedMap<String, String> getHeaders();
String getHeaderString(String name);
Set<String> getAllowedMethods();
Date getDate();
Date getLastModified();
Locale getLanguage();
int getLength();
MediaType getMediaType();
Map<String, NewCookie> getCookies();
EntityTag getEntityTag();
boolean hasEntity();
InputStream getEntityStream();
void setEntityStream(InputStream input);
}Client Filter Usage Example:
// Authentication filter
@Provider
public class BearerTokenFilter implements ClientRequestFilter {
private final String token;
public BearerTokenFilter(String token) {
this.token = token;
}
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add("Authorization", "Bearer " + token);
}
}
// Logging filter
@Provider
public class ClientLoggingFilter implements ClientRequestFilter, ClientResponseFilter {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
System.out.println("Request: " + requestContext.getMethod() + " " + requestContext.getUri());
}
@Override
public void filter(ClientRequestContext requestContext,
ClientResponseContext responseContext) throws IOException {
System.out.println("Response: " + responseContext.getStatus());
}
}
// Register filters
Client client = ClientBuilder.newBuilder()
.register(new BearerTokenFilter("abc123"))
.register(new ClientLoggingFilter())
.build();public class ProcessingException extends RuntimeException {
public ProcessingException(String message);
public ProcessingException(String message, Throwable cause);
public ProcessingException(Throwable cause);
}
public class ResponseProcessingException extends ProcessingException {
public ResponseProcessingException(Response response, String message);
public ResponseProcessingException(Response response, String message, Throwable cause);
public ResponseProcessingException(Response response, Throwable cause);
public Response getResponse();
}Exception Handling Example:
try {
User user = client.target("https://api.example.com/users/123")
.request(MediaType.APPLICATION_JSON)
.get(User.class);
} catch (ProcessingException e) {
// Network or processing error
System.err.println("Request processing failed: " + e.getMessage());
} catch (WebApplicationException e) {
// HTTP error response (4xx, 5xx)
Response response = e.getResponse();
System.err.println("HTTP error " + response.getStatus() + ": " +
response.readEntity(String.class));
}Install with Tessl CLI
npx tessl i tessl/maven-javax-ws-rs--javax-ws-rs-api