Recommended practices for configuring, running, and maintaining the Java SDK.
Keep the SDK up to date
Always use a recent version of the SDK to ensure you have the latest features, performance improvements, and security fixes.
To stay informed about new releases:
- Subscribe to the GitHub releases feed
- Watch the GitHub repository
Store credentials securely
Never hardcode credentials such as
clientId and clientSecret in your source code. Always load them from environment variables or a secrets management service at runtime.String clientId = System.getenv("CTP_CLIENT_ID");
String clientSecret = System.getenv("CTP_CLIENT_SECRET");
String projectKey = System.getenv("CTP_PROJECT_KEY");
ProjectApiRoot apiRoot = ApiRootBuilder.of()
.defaultClient(
ClientCredentials.of()
.withClientId(clientId)
.withClientSecret(clientSecret)
.build(),
ServiceRegion.GCP_EUROPE_WEST1
)
.build(projectKey);
Reuse the API client
The
ProjectApiRoot (and equivalent root objects for other APIs) is thread-safe and designed to be created once and reused across your application. Creating a new client per request is wasteful. Each client holds its own thread pool and connection pool.Create a single instance and inject or share it through your application:
ProjectApiRoot apiRoot = ApiRootBuilder.of()
.defaultClient(
ClientCredentials.of()
.withClientId(System.getenv("CTP_CLIENT_ID"))
.withClientSecret(System.getenv("CTP_CLIENT_SECRET"))
.build(),
ServiceRegion.GCP_EUROPE_WEST1
)
.build(System.getenv("CTP_PROJECT_KEY"));
// Reuse apiRoot throughout the application lifetime
For Spring applications, declare the client as a
@Bean so the framework manages its lifecycle.Close the client
The Java SDK client holds resources including thread pools and IO connections. The
ApiRoot classes implement the Closeable interface and must be closed when your application shuts down.Explicit close
Call
close() explicitly when your application shuts down:// Close the client to release thread pools and IO connections
apiRoot.getApiHttpClient().close();
For Spring, use
@PreDestroy or register a shutdown hook:@PreDestroy
public void closeClient() {
apiRoot.getApiHttpClient().close();
}
try-with-resources
For short-lived operations (such as scripts or batch jobs), use a try-with-resources block to automatically close the client:
try (ProjectApiRoot apiRoot = ApiRootBuilder.of()
.defaultClient(
ClientCredentials.of()
.withClientId(System.getenv("CTP_CLIENT_ID"))
.withClientSecret(System.getenv("CTP_CLIENT_SECRET"))
.build(),
ServiceRegion.GCP_EUROPE_WEST1
)
.build(System.getenv("CTP_PROJECT_KEY"))) {
// Perform operations
Project project = apiRoot.get().executeBlocking().getBody();
System.out.println(project.getKey());
} // client is automatically closed here
Troubleshooting
ApiHttpResponse.getMessage() returns HTTP status text
ApiHttpResponse is an abstraction over the raw HTTP transfer. Its getMessage() method returns the HTTP status reason phrase (such as OK, Bad Request, or Not Found), not the response body.When a request fails, the ErrorMiddleware throws an exception whose message contains the HTTP body. The thrown exceptions extend ApiHttpException, which provides a
getBodyAs() helper to deserialize the error body:apiRoot.products().post(draft).execute()
.exceptionally(throwable -> {
if (throwable instanceof BadRequestException) {
// BadRequestException and ConcurrentModificationException automatically
// deserialize the error response
String message = ((BadRequestException) throwable)
.getErrorResponse()
.getMessage();
}
if (throwable instanceof ApiHttpException) {
// For all other API errors, use getBodyAs() to deserialize manually
String message = ((ApiHttpException) throwable)
.getBodyAs(ErrorResponse.class)
.getMessage();
}
return null;
});
Further resources
- Middleware reference: configure error handling, retry, logging, and more
- Customization guide: configure proxies, timeouts, HTTP client, and authentication flows
- Integration tests: working examples for common SDK operations