Java SDK Overview

Integrate applications running on the Java Virtual Machine (JVM) with Composable Commerce.

The Java SDK (also referred to as the Java v2 SDK) enables you to use Java 8 methods and objects to interact with Composable Commerce APIs rather than using plain HTTP calls and untyped JSON node objects. With the Java SDK, you can make use of IDE autocompletion, type-safety, encapsulation, and an internal domain-specific language for formulating valid requests.

The Java SDK supports multiple languages that run on the JVM, including Java, Scala, Groovy, Clojure, and Kotlin.

Get started

Learn how to set up and use the Java SDK with our get started guide. You can find examples of integration tests for each endpoint here, which include code examples for creating, querying, updating, and deleting resources in your Composable Commerce Project.

Refer to our integration tests for examples of creating, querying, updating, and deleting resources in your Composable Commerce Project.

The Java SDK is fully open source and is available on GitHub under the Apache License, where you can also find its release notes. This repository also includes a full Javadoc reference.

Migration from v1

If you're upgrading from the v1 SDK, our migration guide provides detailed instructions.

SDK features

The Java SDK uses various Java 8 language constructs and classes, including:

As a result of Java 8 dependencies, the Java SDK is unsuitable for Android development as it does not support Java 8 on its virtual machine.

The SDK simplifies development with:

  • Default implementations for equals() and hashCode(): automatically using reflection in model implementation classes.
  • Client interfaces: the HTTP client abstract is a functional interface and can be replaced with test doubles.
  • Model factory methods: each model has a factory method ::of() to create a new empty instance. The method ::builder() returns a new builder instance.
  • Helper methods: you can find a complete list of helper methods including example usage in the Javadoc.

Best practices and error handling

A complete list of best practices can be found on GitHub

You can find integration tests for each endpoint here. These tests also provide code examples for creating, querying, updating, and deleting resources within your Composable Commerce Project.

Exceptions

The Java SDK uses exceptions from the Java Development Kit (such as IllegalArgumentException) and provides its own exceptions which inherit from BaseException.

Problems concerning the ApiHttpClient throw an ApiHttpException.

JSON serializing and deserializing problems throw JsonException.

ApiHttpException is a base exception for all error responses (HTTP status code >= 400).

ApiClientException expresses errors which can be recovered by the client side (HTTP status code >= 400 and < 500). ApiServerException is for server errors.

Errors

If a command cannot be performed due to unfulfilled preconditions, one error response with multiple errors can be returned (for more information, see HTTP API Errors). The Java SDK then adds a BadRequestException into a CompletionStage.

Custom HttpExceptionFactory

Responses with a status code of 400 (or higher) are treated as errors and raise exceptions. These exceptions are created by the DefaultHttpExceptionFactory. In case you want to override the handling you have to implement this interface and override the default methods if necessary.

public class HttpExceptionFactoryTest {
static class CustomExceptionFactory implements HttpExceptionFactory {
@Override
public ApiHttpException create(
ApiHttpRequest request,
ApiHttpResponse<byte[]> response
) {
return new ApiHttpException(
response.getStatusCode(),
request.getSecuredBody(),
response.getHeaders(),
"something bad happened",
response,
request
);
}
@Override
public ResponseSerializer getResponseSerializer() {
return ResponseSerializer.of();
}
}
@Test
public void customFactory() {
ProjectApiRoot client = ApiRootBuilder
.of()
.defaultClient(ServiceRegion.GCP_EUROPE_WEST1.getApiUrl())
.withHttpExceptionFactory(CustomExceptionFactory::new)
.build("my-project-key");
assertThatExceptionOfType(ApiHttpException.class)
.isThrownBy(() -> client.get().executeBlocking())
.withMessageStartingWith("detailMessage: something bad happened");
}
}

Logging

Internal logging used by the commercetools client itself uses a SLF4J logger named commercetools.

The default logger middleware logs the following information per level:

  • Error: logs HTTP method, URI, and response status code for ApiHttpException, and cause and exceptions for all other exceptions.
  • Information: logs HTTP method, URI, response status code, and any deprecation notices. Log level can be configured when instantiating the InternalLoggerMiddleware.
  • Debug: logs string representations of the request and the response, including headers and body, with sensitive data like auth tokens and passwords, redacted.
  • Trace: Logs request and response with pretty-printed output, with sensitive information redacted.

Logger hierarchy

The loggers form a hierarchy separated by a period. The root logger is commercetools. The child loggers of commercetools are the endpoints. For example commercetools.categories for Categories and commercetools.product-types for Product Types.

The grandchild loggers refer to the action. commercetools.categories.request refers to performing requests per HTTPS to commercetools for Categories, commercetools.categories.response refers to the responses.

The logger makes use of different log levels, so for example commercetools.categories.response logs the HTTP response on debug level:

[OkHttp https://api.europe-west1.gcp.commercetools.com/...] DEBUG commercetools.categories.response - io.vrap.rmf.base.client.ApiHttpResponse@33e37acd[statusCode=200,headers=...,textInterpretedBody={"limit":20,"offset":0,"count":4,"total":4,"results":[{"id":"ca7f64dc-ab41-4e7f-b65b-bfc25bf01111","version":1,"createdAt":"2021-01-06T09:21:55.445Z","lastModifiedAt":"2021-01-06T09:21:55.445Z","key":"random-key-15aab6ee-9a48-4fdc-a8c3-8ff9ff390d60","name":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"slug":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"description":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"ancestors":[],"orderHint":"random-string-a99e6941-3225-4766-923a-4a654652532f","externalId":"random-id-100b3851-29b4-47c1-aef3-860b4cd28f54"}]}]

Additionally, commercetools.categories.response logs the formatted HTTP response on trace level:

[OkHttp https://api.europe-west1.gcp.commercetools.com/...] TRACE commercetools.categories.response - 200
{
"limit" : 20,
"offset" : 0,
"count" : 4,
"total" : 4,
"results" : [ {
"id" : "bcb15128-b69e-47b6-81c4-1ea5f9a63b83",
"version" : 1,
"createdAt" : "2021-01-06T09:21:50.485Z",
"lastModifiedAt" : "2021-01-06T09:21:50.485Z",
"createdBy" : {
"clientId" : "h-QvaF3NpsjPBWeXa6TUOnq0",
"isPlatformClient" : false
},
"key" : "random-key-f45535f5-1111-4bfb-98ac-164234ac2c73",
"name" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
},
"slug" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
},
"description" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
},
"ancestors" : [ ],
"orderHint" : "random-string-6b88f299-d4ca-491a-9d7c-3463e718c8d2",
"externalId" : "random-id-b98a5ab4-5413-40b3-8119-eb5ac2b5afbb",
"metaTitle" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
},
"metaKeywords" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
},
"metaDescription" : {
"key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
}
}, {
...
} ]
}

commercetools.products.responses.queries only logs HTTP GET requests and commercetools.products.responses.commands only logs HTTP POST/DELETE requests.

[pool-1-thread-1] DEBUG commercetools.products.request.commands - io.vrap.rmf.base.client.ApiHttpRequest@1d2c6948[method=POST,uri="https://api.europe-west1.gcp.commercetools.com/test-php-dev-integration-1/products",headers=[...],textInterpretedBody={"productType":{"id":"cda39953-23af-4e85-abb0-5b89517ec5f2","typeId":"product-type"},"name":{"random-string-b66de021-d2fa-4262-8837-94a6992a8cdc":"random-string-da28a010-e66b-49d4-a18b-f1bfc145d2f6"},...}]
[pool-1-thread-1] DEBUG commercetools.products.request.queries - io.vrap.rmf.base.client.ApiHttpRequest@53667fdb[method=GET,uri="https://api.europe-west1.gcp.commercetools.com/test-php-dev-integration-1/products/ef745227-b115-4132-ba2c-4e46db80df79",headers=[...],textInterpretedBody=empty body]