A powerful and flexible HTTP client library for Java projects, built on top of Apache HttpComponents. This library provides a clean, fluent API for making HTTP requests with support for retries, OAuth authentication, custom handlers, and comprehensive logging.
- 🌐 Full HTTP Support - GET, POST, PUT, DELETE, and PATCH methods
- 🔄 Retry Mechanism - Configurable retry policies with backoff strategies
- 🔐 Authentication - Built-in support for Basic Auth and OAuth 1.0
- 📝 Multiple Payload Types - JSON, Form data, Multipart, and String entities
- 🎯 Request/Response Handlers - Extensible hooks for request and response processing
- 📊 Structured Logging - Logstash integration for detailed request/response logging
- ⚙️ Flexible Configuration - Custom headers, query parameters, and execution context
- 🛠️ Error Handling - Comprehensive exception handling and retry mechanisms
dependencies {
implementation 'com.javaquery:httpclient:1.0.7'
}<dependency>
<groupId>com.javaquery</groupId>
<artifactId>httpclient</artifactId>
<version>1.0.7</version>
</dependency>importcom.javaquery.http.*;
importcom.javaquery.http.handler.HttpResponseHandler;
// Build the requestHttpRequesthttpRequest = newHttpRequest.HttpRequestBuilder("GetUsers", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/users")
.build();
// Create execution contextHttpExecutionContextcontext = newHttpExecutionContext();
// Execute requestHttpClienthttpClient = newHttpClient();
httpClient.execute(context, httpRequest, newHttpResponseHandler<String>() {
@OverridepublicStringonResponse(HttpResponsehttpResponse) {
returnhttpResponse.getBody();
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
// Handle max retry scenario
}
});StringjsonPayload = "{\"name\":\"John Doe\",\"email\":\"john@example.com\"}";
HttpRequesthttpRequest = newHttpRequest.HttpRequestBuilder("CreateUser", HttpMethod.POST)
.withHost("https://api.example.com")
.withEndPoint("/users")
.withHeader("Content-Type", "application/json")
.withHttpPayload(newHttpRequest.HttpPayload(
StringPool.UTF8, "application/json", jsonPayload
))
.build();
HttpExecutionContextcontext = newHttpExecutionContext();
HttpClienthttpClient = newHttpClient();
httpClient.execute(context, httpRequest, newHttpResponseHandler<JSONObject>() {
@OverridepublicJSONObjectonResponse(HttpResponsehttpResponse) {
returnhttpResponse.getJSONObjectBody();
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
System.err.println("Max retries reached");
}
});HttpRequestgetRequest = newHttpRequest.HttpRequestBuilder("GetRequest", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/resource")
.withQueryParameter("page", "1")
.withQueryParameter("size", "20")
.build();Map<String, Object> formData = newHashMap<>();
formData.put("username", "john.doe");
formData.put("email", "john@example.com");
HttpRequestpostRequest = newHttpRequest.HttpRequestBuilder("PostForm", HttpMethod.POST)
.withHost("https://api.example.com")
.withEndPoint("/submit")
.withHttpPayload(newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/x-www-form-urlencoded",
formData
))
.build();Map<String, Object> multipartData = newHashMap<>();
multipartData.put("file", newFile("/path/to/file.pdf"));
multipartData.put("description", "Document upload");
HttpRequestuploadRequest = newHttpRequest.HttpRequestBuilder("FileUpload", HttpMethod.POST)
.withHost("https://api.example.com")
.withEndPoint("/upload")
.withHttpPayload(newHttpRequest.HttpPayload(
StringPool.UTF8,
"multipart/form-data",
multipartData
))
.build();StringupdatePayload = "{\"status\":\"active\"}";
HttpRequestputRequest = newHttpRequest.HttpRequestBuilder("UpdateUser", HttpMethod.PUT)
.withHost("https://api.example.com")
.withEndPoint("/users/123")
.withHttpPayload(newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/json",
updatePayload
))
.build();HttpRequestdeleteRequest = newHttpRequest.HttpRequestBuilder("DeleteUser", HttpMethod.DELETE)
.withHost("https://api.example.com")
.withEndPoint("/users/123")
.build();HttpRequestauthRequest = newHttpRequest.HttpRequestBuilder("SecureEndpoint", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/secure")
.withBasicAuth("username", "password")
.build();importcom.javaquery.http.oauth.OAuthConfig;
OAuthConfigoauthConfig = OAuthConfig.builder()
.consumerKey("your-consumer-key")
.consumerSecret("your-consumer-secret")
.accessToken("your-access-token")
.accessTokenSecret("your-access-token-secret")
.build();
HttpRequestoauthRequest = newHttpRequest.HttpRequestBuilder("OAuthRequest", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/oauth/resource")
.build();
// Add OAuth handler to execution contextHttpExecutionContextcontext = newHttpExecutionContext();
context.addHttpRequestHandler(newOAuth10HttpRequestHandler(oauthConfig));
httpClient.execute(context, oauthRequest, responseHandler);// Single headerHttpRequestrequest = newHttpRequest.HttpRequestBuilder("Request", HttpMethod.GET)
.withHost("https://api.example.com")
.withHeader("Authorization", "Bearer token123")
.withHeader("X-Custom-Header", "value")
.build();
// Multiple headersMap<String, String> headers = newHashMap<>();
headers.put("Content-Type", "application/json");
headers.put("Accept", "application/json");
HttpRequestrequest = newHttpRequest.HttpRequestBuilder("Request", HttpMethod.GET)
.withHost("https://api.example.com")
.withHeaders(headers)
.build();// Single parameterHttpRequestrequest = newHttpRequest.HttpRequestBuilder("Request", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/search")
.withQueryParameter("q", "java")
.withQueryParameter("limit", "10")
.build();
// Multiple parametersMap<String, String> params = newHashMap<>();
params.put("page", "1");
params.put("size", "20");
params.put("sort", "name");
HttpRequestrequest = newHttpRequest.HttpRequestBuilder("Request", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/users")
.withQueryParameter(params)
.build();importcom.javaquery.http.DefaultRetryPolicy;
HttpRequestrequest = newHttpRequest.HttpRequestBuilder("RetryRequest", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/unstable")
.withRetryPolicy(DefaultRetryPolicy.get(3)) // Retry up to 3 times
.build();importcom.javaquery.http.retry.*;
RetryPolicycustomRetry = newRetryPolicy(
newDefaultRetryCondition(), // When to retrynewDefaultBackoffStrategy(), // How long to wait between retries5// Max retry attempts
);
HttpRequestrequest = newHttpRequest.HttpRequestBuilder("CustomRetry", HttpMethod.GET)
.withHost("https://api.example.com")
.withEndPoint("/resource")
.withRetryPolicy(customRetry)
.build();importcom.javaquery.http.retry.RetryCondition;
publicclassCustomRetryConditionimplementsRetryCondition {
@OverridepublicbooleanshouldRetry(HttpRequestResponsehttpRequestResponse) {
HttpResponseresponse = httpRequestResponse.getHttpResponse();
// Retry on 5xx errors or specific 4xx errorsintstatusCode = response.getStatusCode();
returnstatusCode >= 500 || statusCode == 429 || statusCode == 408;
}
}
RetryPolicyretryPolicy = newRetryPolicy(
newCustomRetryCondition(),
newDefaultBackoffStrategy(),
3
);importcom.javaquery.http.retry.BackoffStrategy;
publicclassExponentialBackoffimplementsBackoffStrategy {
@OverridepubliclongcomputeDelayBeforeNextRetry(HttpRequestResponsehttpRequestResponse, intretriesAttempted) {
// Exponential backoff: 2^attempt * 1000msreturn (long) Math.pow(2, retriesAttempted) * 1000;
}
}
RetryPolicyretryPolicy = newRetryPolicy(
newDefaultRetryCondition(),
newExponentialBackoff(),
5
);importcom.javaquery.http.handler.HttpRequestHandler;
publicclassCustomHeaderHandlerimplementsHttpRequestHandler {
@OverridepublicvoidonRequest(HttpExecutionContextcontext, HttpRequesthttpRequest) {
// Add custom headers before each requesthttpRequest.addHeader("X-Request-ID", UUID.randomUUID().toString());
httpRequest.addHeader("X-Timestamp", String.valueOf(System.currentTimeMillis()));
}
}
// Use the handlerHttpExecutionContextcontext = newHttpExecutionContext();
context.addHttpRequestHandler(newCustomHeaderHandler());
httpClient.execute(context, httpRequest, responseHandler);importcom.javaquery.http.handler.HttpResponseHandler;
HttpResponseHandler<User> userHandler = newHttpResponseHandler<User>() {
@OverridepublicUseronResponse(HttpResponsehttpResponse) {
if (httpResponse.getStatusCode() == 200) {
JSONObjectjson = httpResponse.getJSONObjectBody();
returnparseUser(json);
} elseif (httpResponse.getStatusCode() == 404) {
thrownewUserNotFoundException("User not found");
} else {
thrownewHttpException("Unexpected status: " + httpResponse.getStatusCode());
}
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
LOGGER.error("Failed after maximum retry attempts. Status: {}", httpResponse.getStatusCode());
}
privateUserparseUser(JSONObjectjson) {
// Parse JSON to User objectreturnUser.builder()
.id(json.getLong("id"))
.name(json.getString("name"))
.email(json.getString("email"))
.build();
}
};The HttpExecutionContext allows you to pass metadata and configure handlers for request execution.
HttpExecutionContextcontext = newHttpExecutionContext();
// Add metadataMap<String, Object> metadata = newHashMap<>();
metadata.put("userId", "12345");
metadata.put("requestSource", "mobile-app");
context.setMetaData(metadata);
// Add individual metadatacontext.addMetaData("correlationId", UUID.randomUUID().toString());
httpClient.execute(context, httpRequest, responseHandler);HttpExecutionContextcontext = newHttpExecutionContext();
// Add multiple handlerscontext.addHttpRequestHandler(newAuthHeaderHandler());
context.addHttpRequestHandler(newLoggingHandler());
context.addHttpRequestHandler(newMetricsHandler());
// Or set all at onceList<HttpRequestHandler> handlers = Arrays.asList(
newAuthHeaderHandler(),
newLoggingHandler(),
newMetricsHandler()
);
context.setHttpRequestHandlers(handlers);
httpClient.execute(context, httpRequest, responseHandler);HttpResponseHandler<Object> handler = newHttpResponseHandler<Object>() {
@OverridepublicObjectonResponse(HttpResponsehttpResponse) {
// Get status codeintstatusCode = httpResponse.getStatusCode();
// Get headersMap<String, String> headers = httpResponse.getHeaders();
StringcontentType = headers.get("Content-Type");
// Get response body as stringStringbody = httpResponse.getBody();
// Parse as JSON ObjectJSONObjectjsonObject = httpResponse.getJSONObjectBody();
// Parse as JSON ArrayJSONArrayjsonArray = httpResponse.getJSONArrayBody();
returnbody;
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
// Handle max retry scenario
}
};// Parse as JSONObjectJSONObjectjsonObject = httpResponse.getJSONObjectBody();
Stringname = jsonObject.getString("name");
intage = jsonObject.getInt("age");
// Parse as JSONArrayJSONArrayjsonArray = httpResponse.getJSONArrayBody();
for (inti = 0; i < jsonArray.length(); i++) {
JSONObjectitem = jsonArray.getJSONObject(i);
// Process each item
}importcom.javaquery.http.*;
importcom.javaquery.http.handler.*;
importcom.javaquery.http.retry.*;
publicclassAdvancedHttpClientExample {
publicstaticvoidmain(String[] args) {
// Build request with all optionsMap<String, String> headers = newHashMap<>();
headers.put("Authorization", "Bearer token123");
headers.put("Accept", "application/json");
Map<String, String> queryParams = newHashMap<>();
queryParams.put("page", "1");
queryParams.put("size", "20");
Stringpayload = "{\"action\":\"update\",\"data\":{\"status\":\"active\"}}";
RetryPolicyretryPolicy = newRetryPolicy(
newDefaultRetryCondition(),
newDefaultBackoffStrategy(),
3
);
HttpRequestrequest = newHttpRequest.HttpRequestBuilder("ComplexRequest", HttpMethod.POST)
.withHost("https://api.example.com")
.withPort(443)
.withEndPoint("/api/v1/resources")
.withHeaders(headers)
.withQueryParameter(queryParams)
.withHttpPayload(newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/json",
payload
))
.withRetryPolicy(retryPolicy)
.build();
// Setup execution contextHttpExecutionContextcontext = newHttpExecutionContext();
Map<String, Object> metadata = newHashMap<>();
metadata.put("correlationId", UUID.randomUUID().toString());
metadata.put("requestSource", "backend-service");
context.setMetaData(metadata);
context.addHttpRequestHandler(newHttpRequestHandler() {
@OverridepublicvoidonRequest(HttpExecutionContextctx, HttpRequestreq) {
// Add request timestampreq.addHeader("X-Request-Timestamp", String.valueOf(System.currentTimeMillis()));
}
});
// Execute requestHttpClienthttpClient = newHttpClient();
Resultresult = httpClient.execute(context, request, newHttpResponseHandler<Result>() {
@OverridepublicResultonResponse(HttpResponsehttpResponse) {
if (httpResponse.getStatusCode() >= 200 && httpResponse.getStatusCode() < 300) {
returnparseResult(httpResponse.getJSONObjectBody());
} else {
thrownewHttpException("Request failed: " + httpResponse.getStatusCode());
}
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
LOGGER.error("Max retries reached. Status: {}", httpResponse.getStatusCode());
// Send alert, log to monitoring system, etc.
}
}
);
}
privatestaticResultparseResult(JSONObjectjson) {
// Parse JSON to Result objectreturnnewResult(json);
}
}// JSON payloadHttpRequest.HttpPayloadjsonPayload = newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/json",
"{\"key\":\"value\"}"
);
// Form dataMap<String, Object> formData = newHashMap<>();
formData.put("username", "john");
formData.put("password", "secret");
HttpRequest.HttpPayloadformPayload = newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/x-www-form-urlencoded",
formData
);
// Multipart form dataMap<String, Object> multipartData = newHashMap<>();
multipartData.put("file", newFile("/path/to/file.pdf"));
multipartData.put("description", "Document");
HttpRequest.HttpPayloadmultipartPayload = newHttpRequest.HttpPayload(
StringPool.UTF8,
"multipart/form-data",
multipartData
);
// Plain textHttpRequest.HttpPayloadtextPayload = newHttpRequest.HttpPayload(
StringPool.UTF8,
"text/plain",
"Plain text content"
);
// XMLHttpRequest.HttpPayloadxmlPayload = newHttpRequest.HttpPayload(
StringPool.UTF8,
"application/xml",
"<root><item>value</item></root>"
);try {
httpClient.execute(context, request, newHttpResponseHandler<String>() {
@OverridepublicStringonResponse(HttpResponsehttpResponse) {
intstatusCode = httpResponse.getStatusCode();
if (statusCode >= 200 && statusCode < 300) {
returnhttpResponse.getBody();
} elseif (statusCode == 401) {
thrownewAuthenticationException("Authentication required");
} elseif (statusCode == 403) {
thrownewAuthorizationException("Access denied");
} elseif (statusCode == 404) {
thrownewResourceNotFoundException("Resource not found");
} elseif (statusCode >= 500) {
thrownewServerException("Server error: " + statusCode);
} else {
thrownewHttpException("HTTP error: " + statusCode);
}
}
@OverridepublicvoidonMaxRetryAttempted(HttpResponsehttpResponse) {
// Log or alert when max retries exhaustedthrownewMaxRetriesExceededException(
"Failed after " + retryPolicy.getMaxErrorRetry() + " attempts"
);
}
});
} catch (AuthenticationExceptione) {
// Handle authentication error
} catch (ResourceNotFoundExceptione) {
// Handle not found
} catch (HttpExceptione) {
// Handle general HTTP errors
} catch (Exceptione) {
// Handle unexpected errors
}The HTTP client uses SLF4J for logging and integrates with Logstash for structured logging. Request and response details are automatically logged with correlation information.
Add to your logback.xml:
<configuration>
<appendername="CONSOLE"class="ch.qos.logback.core.ConsoleAppender">
<encoderclass="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<loggername="com.javaquery.http"level="INFO" />
<rootlevel="INFO">
<appender-refref="CONSOLE" />
</root>
</configuration>Logged information includes:
- Request name and method
- URL and endpoint
- Headers and query parameters
- Payload information
- Response status code
- Response time
- Retry attempts
- Metadata from execution context
Builder for creating HTTP requests.
Methods:
withHost(String host)- Set the host URLwithPort(int port)- Set the port numberwithEndPoint(String endPoint)- Set the endpoint pathwithHeader(String key, String value)- Add a single headerwithHeaders(Map<String, String> headers)- Add multiple headerswithQueryParameter(String key, String value)- Add a query parameterwithQueryParameter(Map<String, String> params)- Add multiple query parameterswithBasicAuth(String username, String password)- Set basic authenticationwithHttpPayload(HttpPayload payload)- Set the request payloadwithRetryPolicy(RetryPolicy policy)- Set retry policybuild()- Build the HttpRequest
Response object containing status, headers, and body.
Methods:
int getStatusCode()- Get HTTP status codeMap<String, String> getHeaders()- Get response headersString getBody()- Get response body as stringJSONObject getJSONObjectBody()- Parse body as JSON objectJSONArray getJSONArrayBody()- Parse body as JSON array
Main client for executing requests.
Methods:
<R> R execute(HttpExecutionContext context, HttpRequest request, HttpResponseHandler<R> handler)- Execute HTTP request
Context for request execution with metadata and handlers.
Methods:
void setMetaData(Map<String, Object> metaData)- Set metadata mapvoid addMetaData(String key, Object value)- Add single metadata entryvoid addHttpRequestHandler(HttpRequestHandler handler)- Add request handlervoid setHttpRequestHandlers(List<HttpRequestHandler> handlers)- Set multiple handlers
- Java 11 or higher
- Apache HttpComponents 4.5.14
- SLF4J 2.0.16
- JSON 20250107
- ScribeJava 8.3.3 (for OAuth)
This module depends on:
com.javaquery:util- Utility classes
This project is part of the JLite library suite.
Contributions are welcome! Please ensure all tests pass before submitting pull requests.
javaquery
Current version: 1.0.7
For more information and updates, visit the JLite GitHub repository.