Juneau provides an HTTP client API that makes it extremely simple to connect to remote REST interfaces and
seemlessly send and receive serialized POJOs in requests and responses.
Features:
-
Converts POJOs directly to HTTP request message bodies using {@link org.apache.juneau.serializer.Serializer}
classes.
-
Converts HTTP response message bodies directly to POJOs using {@link org.apache.juneau.parser.Parser}
classes.
-
Exposes the full functionality of the Apache HttpClient API by exposing all methods defined on the
{@link org.apache.http.impl.client.HttpClientBuilder} class.
-
Provides various convenience methods for setting up common SSL and authentication methods.
-
Provides a fluent interface that allows you to make complex REST calls in a single line of code.
The client API is designed to work as a thin layer on top of the proven Apache HttpClient API.
By leveraging the HttpClient library, details such as SSL certificate negotiation, proxies, encoding, etc...
are all handled in Apache code.
The Juneau client API prereq's Apache HttpClient 4.1.2+.
At a minimum, the following jars are required:
httpclient-4.5.jar
httpcore-4.4.1.jar
httpmime-4.5.jar
Example:
// Examples below use the Juneau Address Book resource example
// Create a reusable client with JSON support
RestClient client = RestClient.create().build();
// GET request, ignoring output
try {
int rc = client.doGet("http://localhost:9080/sample/addressBook").run();
// Succeeded!
} catch (RestCallException e) {
// Failed!
System.err.println(
String.format("status=%s, message=%s", e.getResponseStatus(), e.getResponseMessage())
);
}
// Remaining examples ignore thrown exceptions.
// GET request, secure, ignoring output
client.doGet("https://localhost:9443/sample/addressBook").run();
// GET request, getting output as a String. No POJO parsing is performed.
// Note that when calling one of the getX() methods, you don't need to call connect() or disconnect(), since
// it's automatically called for you.
String output = client.doGet("http://localhost:9080/sample/addressBook")
.getResponseAsString();
// GET request, getting output as a Reader
Reader r = client.doGet("http://localhost:9080/sample/addressBook")
.getReader();
// GET request, getting output as an untyped map
// Input must be an object (e.g. "{...}")
ObjectMap m = client.doGet("http://localhost:9080/sample/addressBook/0")
.getResponse(ObjectMap.class);
// GET request, getting output as an untyped list
// Input must be an array (e.g. "[...]")
ObjectList l = client.doGet("http://localhost:9080/sample/addressBook")
.getResponse(ObjectList.class);
// GET request, getting output as a parsed bean
// Input must be an object (e.g. "{...}")
// Note that you don't have to do any casting!
Person p = client.doGet("http://localhost:9080/sample/addressBook/0")
.getResponse(Person.class);
// GET request, getting output as a parsed bean
// Input must be an array of objects (e.g. "[{...},{...}]")
Person[] pa = client.doGet("http://localhost:9080/sample/addressBook")
.getResponse(Person[].class);
// Same as above, except as a List<Person>
List<Person> pl = client.doGet("http://localhost:9080/sample/addressBook")
.getResponse(List.class, Person.class);
// GET request, getting output as a parsed string
// Input must be a string (e.g. "<string>foo</string>" or "'foo'")
String name = client.doGet("http://localhost:9080/sample/addressBook/0/name")
.getResponse(String.class);
// GET request, getting output as a parsed number
// Input must be a number (e.g. "<number>123</number>" or "123")
int age = client.doGet("http://localhost:9080/sample/addressBook/0/age")
.getResponse(Integer.class);
// GET request, getting output as a parsed boolean
// Input must be a boolean (e.g. "<boolean>true</boolean>" or "true")
boolean isCurrent = client.doGet("http://localhost:9080/sample/addressBook/0/addresses/0/isCurrent")
.getResponse(Boolean.class);
// GET request, getting a filtered object
client = RestClient.create().pojoSwaps(CalendarSwap.ISO8601.class).build();
Calendar birthDate = client.doGet("http://localhost:9080/sample/addressBook/0/birthDate")
.getResponse(GregorianCalendar.class);
// PUT request on regular field
String newName = "John Smith";
int rc = client.doPut("http://localhost:9080/addressBook/0/name", newName).run();
// PUT request on filtered field
Calendar newBirthDate = new GregorianCalendar(1, 2, 3, 4, 5, 6);
rc = client.doPut("http://localhost:9080/sample/addressBook/0/birthDate", newBirthDate).run();
// POST of a new entry to a list
Address newAddress = new Address("101 Main St", "Anywhere", "NY", 12121, false);
rc = client.doPost("http://localhost:9080/addressBook/0/addresses", newAddress).run();
Notes:
-
The {@link org.apache.juneau.rest.client.RestClient} class exposes all the builder methods on the Apache
HttpClient {@link org.apache.http.impl.client.HttpClientBuilder} class.
Use these methods to provide any customized HTTP client behavior.
1.1 - SSL Support
The simplest way to enable SSL support in the client is to use the
{@link org.apache.juneau.rest.client.RestClientBuilder#enableSSL(SSLOpts)} method and one of the predefined
{@link org.apache.juneau.rest.client.SSLOpts} instances:
- {@link org.apache.juneau.rest.client.SSLOpts#DEFAULT} - Normal certificate and hostname validation.
- {@link org.apache.juneau.rest.client.SSLOpts#LAX} - Allows for self-signed certificates.
Example:
// Create a client that ignores self-signed or otherwise invalid certificates.
RestClientBuilder builder = RestClient.create()
.enableSSL(SSLOpts.LAX);
// ...or...
RestClientBuilder builder = RestClient.create()
.enableLaxSSL();
This is functionally equivalent to the following:
RestClientBuilder builder = RestClient.create();
HostnameVerifier hv = new NoopHostnameVerifier();
TrustManager tm = new SimpleX509TrustManager(true);
for (String p : new String[]{"SSL","TLS","SSL_TLS"}) {
SSLContext ctx = SSLContext.getInstance(p);
ctx.init(null, new TrustManager[] { tm }, null);
SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(ctx, hv);
builder.setSSLSocketFactory(sf);
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>.create()
.register("https", sf).build();
builder.setConnectionManager(new PoolingHttpClientConnectionManager(r));
}
More complex SSL support can be enabled through the various {@link org.apache.http.impl.client.HttpClientBuilder}
methods defined on the class.
1.1.1 - SSLOpts Bean
The {@link org.apache.juneau.rest.client.SSLOpts} class itself is a bean that can be created by the
parsers.
For example, SSL options can be specified in a config file and retrieved as a bean using the
{@link org.apache.juneau.ini.ConfigFile} class.
#================================================================================
# My Connection Settings
#================================================================================
[Connection]
url = https://myremotehost:9443
ssl = {certValidate:'LAX',hostVerify:'LAX'}
// Read config file and set SSL options based on what's in that file.
ConfigFile cf = ConfigFile.create().build("MyConfig.cfg");
SSLOpts ssl = cf.getObject(SSLOpts.class, "Connection/ssl");
RestClient rc = new RestClient().enableSSL(ssl);
1.2 - Authentication
1.2.1 - BASIC Authentication
The {@link org.apache.juneau.rest.client.RestClientBuilder#basicAuth(String,int,String,String)} method
can be used to quickly enable BASIC authentication support.
Example:
// Create a client that performs BASIC authentication using the specified user/pw.
RestClient restClient = RestClient.create()
.basicAuth(HOST, PORT, USER, PW)
.build();
This is functionally equivalent to the following:
RestClientBuilder builder = RestClient.create();
AuthScope scope = new AuthScope(HOST, PORT);
Credentials up = new UsernamePasswordCredentials(USER, PW);
CredentialsProvider p = new BasicCredentialsProvider();
p.setCredentials(scope, up);
builder.setDefaultCredentialsProvider(p);
1.2.2 - FORM-based Authentication
The {@link org.apache.juneau.rest.client.RestClientBuilder} class does not itself provide FORM-based
authentication since there is no standard way of providing such support.
Typically, to perform FORM-based or other types of authentication, you'll want to create your own
subclass of {@link org.apache.juneau.rest.client.RestClientBuilder} and override the
{@link org.apache.juneau.rest.client.RestClientBuilder#createHttpClient()} method to provide an
authenticated client.
The following example shows how the JazzRestClient
class provides FORM-based
authentication support.
/**
* Constructor.
*/
public JazzRestClientBuilder(URI jazzUri, String user, String pw) throws IOException {
...
}
/**
* Override the createHttpClient() method to return an authenticated client.
*/
@Override /* RestClientBuilder */
protected CloseableHttpClient createHttpClient() throws Exception {
CloseableHttpClient client = super.createHttpClient();
formBasedAuthenticate(client);
visitAuthenticatedURL(client);
return client;
}
/*
* Performs form-based authentication against the Jazz server.
*/
private void formBasedAuthenticate(HttpClient client) throws IOException {
URI uri2 = jazzUri.resolve("j_security_check");
HttpPost request = new HttpPost(uri2);
request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
// Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
NameValuePairs params = new NameValuePairs()
.append(new BasicNameValuePair("j_username"", user))
.append(new BasicNameValuePair("j_password", pw));
request.setEntity(new UrlEncodedFormEntity(params));
HttpResponse response = client.execute(request);
try {
int rc = response.getStatusLine().getStatusCode();
Header authMsg = response.getFirstHeader("X-com-ibm-team-repository-web-auth-msg");
if (authMsg != null)
throw new IOException(authMsg.getValue());
// The form auth request should always respond with a 200 ok or 302 redirect code
if (rc == SC_MOVED_TEMPORARILY) {
if (response.getFirstHeader("Location").getValue().matches("^.*/auth/authfailed.*$"))
throw new IOException("Invalid credentials.");
} else if (rc != SC_OK) {
throw new IOException("Unexpected HTTP status: " + rc);
}
} finally {
EntityUtils.consume(response.getEntity());
}
}
/*
* This is needed for Tomcat because it responds with SC_BAD_REQUEST when the j_security_check URL is visited before an
* authenticated URL has been visited. This same URL must also be visited after authenticating with j_security_check
* otherwise tomcat will not consider the session authenticated
*/
private int visitAuthenticatedURL(HttpClient httpClient) throws IOException {
HttpGet authenticatedURL = new HttpGet(jazzUri.resolve("authenticated/identity"));
HttpResponse response = httpClient.execute(authenticatedURL);
try {
return response.getStatusLine().getStatusCode();
} finally {
EntityUtils.consume(response.getEntity());
}
}
1.2.3 - OIDC Authentication
The following example shows how the JazzRestClient
class provides OIDC authentication
support.
/**
* Constructor.
*/
public JazzRestClientBuilder(URI jazzUri, String user, String pw) throws IOException {
...
}
/**
* Override the createHttpClient() method to return an authenticated client.
*/
@Override /* RestClientBuilder */
protected CloseableHttpClient createHttpClient() throws Exception {
CloseableHttpClient client = super.createHttpClient();
oidcAuthenticate(client);
return client;
}
private void oidcAuthenticate(HttpClient client) throws IOException {
HttpGet request = new HttpGet(jazzUri);
request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
// Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
HttpResponse response = client.execute(request);
try {
int code = response.getStatusLine().getStatusCode();
// Already authenticated
if (code == SC_OK)
return;
if (code != SC_UNAUTHORIZED)
throw new RestCallException("Unexpected response during OIDC authentication: "
+ response.getStatusLine());
// x-jsa-authorization-redirect
String redirectUri = getHeader(response, "X-JSA-AUTHORIZATION-REDIRECT");
if (redirectUri == null)
throw new RestCallException("Expected a redirect URI during OIDC authentication: "
+ response.getStatusLine());
// Handle Bearer Challenge
HttpGet method = new HttpGet(redirectUri + "&prompt=none");
addDefaultOidcHeaders(method);
response = client.execute(method);
code = response.getStatusLine().getStatusCode();
if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 2: "
+ response.getStatusLine());
String loginRequired = getHeader(response, "X-JSA-LOGIN-REQUIRED");
if (! "true".equals(loginRequired))
throw new RestCallException("X-JSA-LOGIN-REQUIRED header not found on response during OIDC authentication phase 2: "
+ response.getStatusLine());
method = new HttpGet(redirectUri + "&prompt=none");
addDefaultOidcHeaders(method);
response = client.execute(method);
code = response.getStatusLine().getStatusCode();
if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 3: "
+ response.getStatusLine());
// Handle JAS Challenge
method = new HttpGet(redirectUri);
addDefaultOidcHeaders(method);
response = client.execute(method);
code = response.getStatusLine().getStatusCode();
if (code != SC_OK)
throw new RestCallException("Unexpected response during OIDC authentication phase 4: "
+ response.getStatusLine());
cookie = getHeader(response, "Set-Cookie");
Header[] defaultHeaders = new Header[] {
new BasicHeader("User-Agent", "Jazz Native Client"),
new BasicHeader("X-com-ibm-team-configuration-versions",
"com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0"),
new BasicHeader("Accept", "text/json"),
new BasicHeader("Authorization", "Basic "
+ StringUtils.base64EncodeToString(user + ":" + pw)),
new BasicHeader("Cookie", cookie)
};
setDefaultHeaders(Arrays.asList(defaultHeaders));
} finally {
EntityUtils.consume(response.getEntity());
}
}
private void addDefaultOidcHeaders(HttpRequestBase method) {
method.addHeader("User-Agent", "Jazz Native Client");
method.addHeader("X-com-ibm-team-configuration-versions",
"com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0");
method.addHeader("Accept", "text/json");
if (cookie != null) {
method.addHeader("Authorization", "Basic "
+ StringUtils.base64EncodeToString(user + ":" + pw));
method.addHeader("Cookie", cookie);
}
}
1.3 - Using Response Patterns
One issue with REST (and HTTP in general) is that the HTTP response code must be set as a header before the
body of the request is sent. This can be problematic when REST calls invoke long-running processes, pipes
the results through the connection, and then fails after an HTTP 200 has already been sent.
One common solution is to serialize some text at the end to indicate whether the long-running process
succeeded (e.g. "FAILED" or "SUCCEEDED").
The {@link org.apache.juneau.rest.client.RestClient} class has convenience methods for scanning the
response without interfering with the other methods used for retrieving output.
The following example shows how the {@link org.apache.juneau.rest.client.RestCall#successPattern(String)}
method can be used to look for a SUCCESS message in the output:
Example:
// Throw a RestCallException if SUCCESS is not found in the output.
restClient.doPost(URL)
.successPattern("SUCCESS")
.run();
The {@link org.apache.juneau.rest.client.RestCall#failurePattern(String)} method does the opposite.
It throws an exception if a failure message is detected.
Example:
// Throw a RestCallException if FAILURE or ERROR is found in the output.
restClient.doPost(URL)
.failurePattern("FAILURE|ERROR")
.run();
These convenience methods are specialized methods that use the
{@link org.apache.juneau.rest.client.RestCall#responsePattern(ResponsePattern)} method which uses regular
expression matching against the response body.
This method can be used to search for arbitrary patterns in the response body.
The following example shows how to use a response pattern finder to find and capture patterns for
"x=number" and "y=string" from a response body.
Example:
final List<Number> xList = new ArrayList<Number>();
final List<String> yList = new ArrayList<String>();
String responseText = restClient.doGet(URL)
.addResponsePattern(
new ResponsePattern("x=(\\d+)") {
@Override
public void onMatch(RestCall restCall, Matcher m) throws RestCallException {
xList.add(Integer.parseInt(m.group(1)));
}
@Override
public void onNoMatch(RestCall restCall) throws RestCallException {
throw new RestCallException("No X's found!");
}
}
)
.addResponsePattern(
new ResponsePattern("y=(\\S+)") {
@Override
public void onMatch(RestCall restCall, Matcher m) throws RestCallException {
yList.add(m.group(1));
}
@Override
public void onNoMatch(RestCall restCall) throws RestCallException {
throw new RestCallException("No Y's found!");
}
}
)
.getResponseAsString();
Using response patterns does not affect the functionality of any of the other methods
used to retrieve the response such as {@link org.apache.juneau.rest.client.RestCall#getResponseAsString()}
or {@link org.apache.juneau.rest.client.RestCall#getResponse(Class)}.
HOWEVER, if you want to retrieve the entire text of the response from inside the match methods,
use {@link org.apache.juneau.rest.client.RestCall#getCapturedResponse()} since this method will not absorb
the response for those other methods.
1.4 - Piping Response Output
The {@link org.apache.juneau.rest.client.RestCall} class provides various convenience pipeTo()
methods to pipe output to output streams and writers.
If you want to pipe output without any intermediate buffering, you can use the
{@link org.apache.juneau.rest.client.RestCall#byLines()} method.
This will cause the output to be piped and flushed after every line.
This can be useful if you want to display the results in real-time from a long running process producing
output on a REST call.
Example:
// Pipe output from REST call to System.out in real-time.
restClient.doPost(URL).byLines().pipeTo(new PrintWriter(System.out)).run();
1.5 - Debugging
Use the {@link org.apache.juneau.rest.client.RestClientBuilder#debug()} method to enable logging for HTTP requests
made from the client.
Under-the-covers, this is simply a shortcut for adding the {@link org.apache.juneau.rest.client.RestCallLogger#DEFAULT}
intercepter to the client.
This causes the following output to be generated by the Java org.apache.juneau.rest.client
logger at WARNING level:
=== HTTP Call (outgoing) =======================================================
=== REQUEST ===
POST http://localhost:10000/testUrl HTTP/1.1
---request headers---
Debug: true
No-Trace: true
Accept: application/json
---request entity---
Content-Type: application/json
---request content---
{"foo":"bar","baz":123}
=== RESPONSE ===
HTTP/1.1 200 OK
---response headers---
Content-Type: application/json;charset=utf-8
Content-Length: 21
Server: Jetty(8.1.0.v20120127)
---response content---
{"message":"OK then"}
=== END ========================================================================
This setting also causes a Debug: true
header value to trigger logging of the request on the
server side as well.
=== HTTP Request (incoming) ====================================================
HTTP POST /testUrl
---Headers---
Host: localhost:10000
Transfer-Encoding: chunked
Accept: application/json
Content-Type: application/json
User-Agent: Apache-HttpClient/4.5 (Java/1.6.0_65)
Connection: keep-alive
Debug: true
Accept-Encoding: gzip,deflate
---Default Servlet Headers---
---Body---
{"foo":"bar","baz":123}
=== END ========================================================================
1.6 - Logging
Use the {@link org.apache.juneau.rest.client.RestClientBuilder#logTo(Level,Logger)} and
{@link org.apache.juneau.rest.client.RestCall#logTo(Level,Logger)} methods to log HTTP calls.
These methods will cause the HTTP request and response headers and body to be logged to the specified logger.
Example:
// Log the HTTP request/response to the specified logger.
int rc = restClient.doGet(URL).logTo(INFO, getLogger()).run();
The method call is ignored if the logger level is below the specified level.
Customized logging can be handled by sub-classing the {@link org.apache.juneau.rest.client.RestCallLogger}
class and using the {@link org.apache.juneau.rest.client.RestCall#intercepter(RestCallInterceptor)} method.
1.7 - Interceptors
The {@link org.apache.juneau.rest.client.RestClientBuilder#intercepter(RestCallInterceptor)} and
{@link org.apache.juneau.rest.client.RestCall#intercepter(RestCallInterceptor)} methods can be used to
intercept responses during specific connection lifecycle events.
The {@link org.apache.juneau.rest.client.RestCallLogger} class is an example of an intercepter that uses
the various lifecycle methods to log HTTP requests.
/**
* Specialized intercepter for logging calls to a log file.
*/
public class RestCallLogger extends RestCallInterceptor {
private Level level;
private Logger log;
/**
* Constructor.
*
* @param level The log level to log messages at.
* @param log The logger to log to.
*/
protected RestCallLogger(Level level, Logger log) {
this.level = level;
this.log = log;
}
@Override /* RestCallInterceptor */
public void onInit(RestCall restCall) {
if (log.isLoggable(level))
restCall.captureResponse();
}
@Override /* RestCallInterceptor */
public void onConnect(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res) {
// Do nothing.
}
@Override /* RestCallInterceptor */
public void onRetry(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res) {
if (log.isLoggable(level))
log.log(level, MessageFormat.format("Call to {0} returned {1}. Will retry.", req.getRequestLine().getUri(), statusCode));
}
@Override /* RestCallInterceptor */
public void onClose(RestCall restCall) throws RestCallException {
try {
if (log.isLoggable(level)) {
String output = restCall.getCapturedResponse();
StringBuilder sb = new StringBuilder();
HttpUriRequest req = restCall.getRequest();
HttpResponse res = restCall.getResponse();
if (req != null) {
sb.append("\n=== HTTP Call (outgoing) =========================================================");
sb.append("\n=== REQUEST ===\n").append(req);
sb.append("\n---request headers---");
for (Header h : req.getAllHeaders())
sb.append("\n").append(h);
if (req instanceof HttpEntityEnclosingRequestBase) {
sb.append("\n---request entity---");
HttpEntityEnclosingRequestBase req2 = (HttpEntityEnclosingRequestBase)req;
HttpEntity e = req2.getEntity();
if (e == null)
sb.append("\nEntity is null");
else {
if (e.getContentType() != null)
sb.append("\n").append(e.getContentType());
if (e.getContentEncoding() != null)
sb.append("\n").append(e.getContentEncoding());
if (e.isRepeatable()) {
try {
sb.append("\n---request content---\n").append(EntityUtils.toString(e));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
}
}
if (res != null) {
sb.append("\n=== RESPONSE ===\n").append(res.getStatusLine());
sb.append("\n---response headers---");
for (Header h : res.getAllHeaders())
sb.append("\n").append(h);
sb.append("\n---response content---\n").append(output);
sb.append("\n=== END ========================================================================");
}
log.log(level, sb.toString());
}
} catch (IOException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
}
}
1.8 - Remoteable Proxies
Juneau provides the capability of calling methods on POJOs on a server through client-side proxy interfaces.
It offers a number of advantages over other similar remote proxy interfaces, such as being much simpler to
use and allowing much more flexibility.
Proxy interfaces are retrieved using the {@link org.apache.juneau.rest.client.RestClient#getRemoteableProxy(Class)}
method.
The remoteable servlet is a specialized subclass of {@link org.apache.juneau.rest.RestServlet} that
provides a full-blown REST interface for calling interfaces remotely.
In this example, we have the following interface defined that we want to call from the client side against
a POJO on the server side (i.e. a Remoteable Service)...
public interface IAddressBook {
Person createPerson(CreatePerson cp) throws Exception;
}
The client side code for invoking this method is shown below...
// Create a RestClient using JSON for serialization, and point to the server-side remoteable servlet.
RestClient client = RestClient.create()
.rootUrl("https://localhost:9080/juneau/sample/remoteable")
.build();
// Create a proxy interface.
IAddressBook ab = client.getRemoteableProxy(IAddressBook.class);
// Invoke a method on the server side and get the returned result.
Person p = ab.createPerson(
new CreatePerson("Test Person",
AddressBook.toCalendar("Aug 1, 1999"),
new CreateAddress("Test street", "Test city", "Test state", 12345, true))
);
The requirements for a method to be callable through a remoteable service are:
One significant feature is that the remoteable services servlet is a full-blown REST interface.
Therefore, in cases where the interface classes are not available on the client side, the same method calls
can be made through pure REST calls.
This can also aid significantly in debugging since calls to the remoteable service can be called directly
from a browser with no code involved.
See org.apache.juneau.rest.remoteable
for more information.
1.9 - Other Useful Methods
The {@link org.apache.juneau.rest.client.RestClientBuilder#rootUrl(Object)} method can be used to specify a
root URL on all requests so that you don't have to use absolute paths on individual calls.
// Create a rest client with a root URL
RestClient rc = RestClient.create().rootUrl("http://localhost:9080/foobar").build();
String r = rc.doGet("/baz").getResponseAsString(); // Gets "http://localhost:9080/foobar/baz"
The {@link org.apache.juneau.rest.client.RestClientBuilder#property(String,Object)} method can be used to
set serializer and parser properties.
For example, if you're parsing a response into POJOs and you want to ignore fields that aren't on the
POJOs, you can use the {@link org.apache.juneau.BeanContext#BEAN_ignoreUnknownBeanProperties} property.
// Create a rest client that ignores unknown fields in the response
RestClient rc = RestClient.create()
.property(BEAN_ignoreUnknownBeanProperties, true)
// or .ignoreUnknownBeanProperties(true)
.build();
MyPojo myPojo = rc.doGet(URL).getResponse(MyPojo.class);
The {@link org.apache.juneau.rest.client.RestCall#retryable(int,long,RetryOn)} method can be used to
automatically retry requests on failures.
This can be particularly useful if you're attempting to connect to a REST resource that may be in the
process of still initializing.
// Create a rest call that retries every 10 seconds for up to 30 minutes as long as a connection fails
// or a 400+ is received.
restClient.doGet(URL)
.retryable(180, 10000, RetryOn.DEFAULT)
.run();