commercetools JVM SDK - The e-commerce SDK from commercetools running on the Java virtual machine.

About the JVM SDK

The JVM SDK enables developers to use Java 8 methods and objects to communicate with the commercetools platform rather than using plain HTTP calls and untyped JSON node objects. Users gain type-safety, encapsulation, IDE auto completion and an internal domain specific language to discover and formulate valid requests.

It is called “JVM” SDK and not “Java” SDK because it addresses multiple languages that run on the JVM like Scala, Groovy, Clojure and Kotlin. In addition it uses a lot of Java 8 language constructs and classes which to provide all the great features. As a result to the Java 8 dependency the JVM SDK is not usable for the Android Platform which is not (yet) supporting Java 8 on its virtual machine.

The JVM SDK is already the second generation Java SDK, the previous Play SDK was retired since it only supported one Scala and Play version at a time and thus forced to much Play version migration overhead. The JVM SDK is more flexible due to its dependencies and architecture.

Link list for the impatient:

Features

Parallelity Features

API calls can take time and to save CPU cycles threads are not blocked.

Parallel execution

For high performance you should parallelize as much requests as possible. By using java.util.concurrent.CompletionStage from Java it is easy to start parallel asynchronous calls and combine them into a single CompletionStage.

final CompletionStage<Product> productStage =
    client.execute(ProductByIdGet.of("product-id"));
final CompletionStage<Cart> cartStage =
    client.execute(CartByCustomerIdGet.of("customer-id"));
return productStage.thenCombine(cartStage, (Product product, Cart cart) -> {
    final String productData = "product: " + product;
    final String cartData = "cart: " + cart;
    return renderTemplate(productData + " " + cartData);
});

Recover from Exceptions

API requests can fail due to network errors and other sources. With the JVM SDK it is easy to code a plan B instead of crashing.

final CompletionStage<Product> productStage =
    client.execute(ProductByIdGet.of("product-id"));
final CompletionStage<Html> htmlStage = productStage
    .thenApply((Product product) -> renderTemplate("product: " + product));
final CompletionStage<Html> failSafeHtmlStage =
    htmlStage.exceptionally(
        (Throwable t) -> renderTemplate("Ooops, an error occured."));

Other future implementations (Scala)

There are add-ons to support other Future implementations such as Scala’s Future (2.10, 2.11, 2.12) and Play Frameworks F.Promise (2.2, 2.3, 2.4, 2.5). We also plan to support Spring, Rx and Reactive Streams.

import scala.concurrent.Future
val future: Future[PagedSearchResult[ProductProjection]] =
    scalaSphereClient(ProductProjectionSearch.ofCurrent())

Library Features

Java Money

The SDK uses the Java Money library which makes it easy to retrieve currencies and format monetary amounts for specific locales.

final MonetaryAmount money = MoneyImpl.ofCents(123456, "USD");
assertThat(MonetaryFormats.getAmountFormat(GERMANY).format(money))
    .as("German decimal separator is used")
    .isEqualTo("1.234,56 USD");
assertThat(MonetaryFormats.getAmountFormat(US).format(money))
    .as("in US currency comes first")
    .isEqualTo("USD1,234.56");
assertThat(Monetary.getCurrency(GERMANY))
    .as("find default currency for a country")
    .isEqualTo(EUR);
assertThat(Monetary.getCurrency(US)).isEqualTo(USD);

Java 8 time classes

In the commercetools platform models the Java time classes are used and that can be conveniently formatted for specific time zones.

final ZonedDateTime dateTime = ZonedDateTime.parse("2015-07-09T07:46:40.230Z");
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
assertThat(dateTime.withZoneSameInstant(ZoneId.of("Europe/Berlin"))
    .format(formatter))
    .isEqualTo("09.07.2015 09:46");
assertThat(dateTime.withZoneSameInstant(ZoneId.of("America/New_York"))
    .format(formatter))
    .isEqualTo("09.07.2015 03:46");

Country codes

In the API country codes are represented as Strings, in the JVM SDK models it is com.neovisionaries.i18n.CountryCode from the nv-i18n library. With the library you can format the country name according to a locale.

final CountryCode countryCode = CountryCode.US;
assertThat(countryCode.toLocale().getDisplayCountry(US))
    .isEqualTo("United States");
assertThat(countryCode.toLocale().getDisplayCountry(GERMANY))
    .isEqualTo("Vereinigte Staaten von Amerika");
assertThat(countryCode.toLocale().getDisplayCountry(FRANCE))
    .isEqualTo("Etats-Unis");
assertThat(countryCode.getAlpha2())
    .isEqualTo("US");

Logging Features

For logging SLF4J is used.

For each commercetools platform resource you can specify a custom log level. Moreover, it is possible to set the level for request or response objects and fine-tune them for read and write access to the API.

The trace level logs the JSON objects in a pretty printed way - this way you can directly analyze what was sent to or received from the commercetools platform HTTP API.

Domain Specific Languages for creating requests

Product Search is one of the most important parts of a web shop and with the JVM SDK you will be supported to create powerful requests.

final ProductProjectionSearch searchRequest =
    ProductProjectionSearch.ofCurrent()
    .withText(Locale.ENGLISH, "shoes")
    .withQueryFilters(m -> m.allVariants()
    .attribute().ofEnum("size").key().is("M"))
    .plusFacets(m -> m.categories().id().allTerms())
    .withSort(m -> m.name().locale(ENGLISH).asc());

Query

Creating queries for deep nested objects works like a charm.

OrderQuery.of()
    .withPredicates(m -> m.customerGroup()
    .id().is("customer-group-id"))
    .plusPredicates(m -> m.shipmentState()
    .isNot(ShipmentState.SHIPPED))
    .withSort(m -> m.createdAt().sort().asc());

Reference Expansion

Fetching multiple objects in in request can be done by reference expansion.

final ReviewByIdGet reviewRequest = ReviewByIdGet.of("review-id")
    .plusExpansionPaths(m -> m.customer());
final Review review = client.executeBlocking(reviewRequest);
final Customer customer = review.getCustomer().getObj();

Type-safety

In a useful way objects are typed and so the compiler checks for some copy paste errors.

final QueryPredicate<Review> predicate = //review context
    ReviewQueryModel.of().id().is("review-id");
    ReviewQuery.of()
    .plusPredicates(predicate);//compiles
//OrderQuery.of()//wrong context
//.plusPredicates(predicate);//doesn't compile

Flexibility above the JVM SDK core

Even if a predicate, an expansion path or a sort expression is not yet supported in the SDK, String expressions can be used as fallback.

final QueryPredicate<Product> safePredicate = ProductQueryModel.of()
    .masterData().current().name()
    .lang(ENGLISH).isIn(asList("foo", "bar"));
final String s =
    "masterData(current(name(en in (\"foo\", \"bar\"))))";
final QueryPredicate<Product> unsafePredicate =
    QueryPredicate.of(s);
assertThat(unsafePredicate).isEqualTo(safePredicate);

Immutability

Immutable objects can be freely shared in a multi-threaded environment. The JVM SDK resource types are immutable by default. Also typical requests are immutable and provide an API to create adjusted copies which fit well in a functional programming style.

final ProductProjection product = getProduct();
//product.setName("new name");//no bean setter

final CategoryQuery query = CategoryQuery.of();
final CategoryQuery query2 = query.withLimit(30);
assertThat(query == query2).isFalse();

Modularity Features

It is possible to just use the SDK to get an access token

The JVM SDK enables you to fetch an access token for a commercetools platform project.

final SphereAuthConfig authConfig = SphereAuthConfig
    .of("project-key", "clientId", "clientSecret");
final CompletionStage<String> accesstokenStage =
    TokensFacade.fetchAccessToken(authConfig);

Java models not required but provided

If the Java models don’t fit you use cases you can create your own model classes or just use directly JSON in Java.

final ProductProjectionSearch searchWithJavaModel =
    ProductProjectionSearch.ofStaged()
    .withPriceSelection(PriceSelection.of(EUR))
    .withExpansionPaths(m -> m.categories())
    .withSort(m -> m.createdAt().desc())
    .withLimit(10);
final PagedSearchResult<ProductProjection> result =
    client.executeBlocking(searchWithJavaModel);
final JsonNodeSphereRequest jsonNodeSphereRequest =
    JsonNodeSphereRequest.of(searchWithJavaModel);
assertThat(searchWithJavaModel.httpRequestIntent())
    .isEqualTo(jsonNodeSphereRequest.httpRequestIntent());
//different output
final JsonNode jsonNode =
    client.executeBlocking(jsonNodeSphereRequest);

Testable request and response handling

If the Java models don’t fit you use cases you can create your own model classes or just use directly JSON in Java.

final SphereClient asyncClient = TestDoubleSphereClientFactory
    .createHttpTestDouble(httpRequest ->
    HttpResponse.of(200, "{\n" +
    "    \"id\" : \"category-id\",\n" +
    "    \"version\" : 1,\n" +
    "    \"name\" : {\n" +
    "        \"en\" : \"engl. name\"\n" +
    "    },\n" +
    "    \"slug\" : {\n" +
    "        \"en\" : \"slug\"\n" +
    "    }\n" +
    "}"));
final BlockingSphereClient client = BlockingSphereClient
    .of(asyncClient, 3, TimeUnit.SECONDS);
final Category category =
    client.executeBlocking(CategoryByIdGet.of("category-id"));
assertThat(category.getName().get(ENGLISH)).isEqualTo("engl. name");

JVM SDK supports multiple HTTP client implementation for compatibility and speed

Async HTTP client is a very fast and verbose HTTP client but has problems in some projects concerning compatibility of this library and to the netty library. This can be solved by picking a specific version or fall back to the Apache HTTP client. Also it is possible to configure the underlying client for example to deal with proxies.

final String[] enabledProtocols = {"TLSv1.1", "TLSv1.2"};
final DefaultAsyncHttpClientConfig config =
    new DefaultAsyncHttpClientConfig.Builder()
    .setEnabledProtocols(enabledProtocols)
    .build();
final Supplier<HttpClient> httpClientSupplier =
    () -> AsyncHttpClientAdapter.of(new DefaultAsyncHttpClient(config));
final SphereClientFactory factory =
    SphereClientFactory.of(httpClientSupplier);
final SphereClient client =
    factory.createClient("project-key", "clientId", "clientSec");

Dependencies

There are hard dependencies to

The HTTP underlying client can be selected from

The JVM SDK core does not depend on Play Framework, Scala, Spring or Groovy.

Getting the JVM SDK

We release artifacts to Maven Central, refer to the JVM SDK GitHub repository for getting the dependency.

Examples and Templates

Please check out the following examples and templates. They are a very good way to start using our platform.

All examples and templates are open-source software. The code is publicly available on GitHub. Artifacts are also published on Maven Central.

Commercetools Hello Maven Archetype

Simple Maven archetype to create a Java application with a main method which does a request to the commercetools platform. It prints the product names and the category names it is in.

For more information visit its GitHub repo.

Commercetools Hello Example with Gradle

The repository commercetools-hello-api-java shows how to use the JVM SDK with a Gradle 2 project. It prints out the product names and the names of the categories the product is assigned to.

Commercetools Hello Scala Activator Template

The template provides a minimal Scala application which uses Scala Futures instead of the Java CompletionStages and shows the usage of Scala closures to express product queries.

Reproducer App

The reproducer app is a playground to get to know exceptions in the JVM SDK as well as fine tune the log level.

Spring MVC Archetype

This archetype is a simple example integrating the commercetools JVM SDK with Spring MVC and Spring DI. It just shows some products using an async API.

Sunrise Play Java Shop

Sunrise Java is a template shop, that will be used by partners, developers or anyone who wants to start a project with the commercetools platform.

Donut

Donut Store is a free template for subscription e-commerce sites and it’s built on top of the APIs of Pactas, Paymill and the commercetools platform.

Integrations

Play Framework Integration

Play Frameworks uses a proprietary “future” (F.Promise = async result object) implementation for the versions 2.2, 2.3, 2.4 and 2.5. Commercetools provides a wrapper for the commercetools client to produce F.Promise instead of CompletionStage. In Play Framework 2.5 F.Promises are deprecated and CompletionStage is used instead so this module is not required anymore.

Scala Integration

The Scala add-ons for the JVM SDK provides as future implementation scala.concurrent.Future instead of CompletionStage. Also it enables to use the domain specific language of the JVM SDK to formulate requests. With the current Scala version Java 8 lambdas cannot directly expressed in Scala.

Reactive Streams Integration

The reactive streams add-ons provide facilities to fetch all resources of a certain criteria with transparently handling pagination.

Example to create a reactive streams publisher:

import io.sphere.sdk.client.SphereClient;
import io.sphere.sdk.products.Product;
import io.sphere.sdk.products.queries.ProductQuery;
import io.sphere.sdk.reactivestreams.SphereReactiveStreams;
import org.reactivestreams.Publisher;

public class QueryAllIdentifiableDemo {
    public static void demo(final SphereClient client) {
        final Publisher<Product> productPublisher =
            SphereReactiveStreams.publisherOf(ProductQuery.of(), client);
    }
}

The repository commercetools-android-example shows an Android app without the JVM SDK how to search and sort products.