Announcement: You can find the guides for Commerce 7.5 and later on the new Elastic Path Documentation site. This site contains the guides for Commerce 6.13.0 through 7.4.1.Visit new site

This version of Elastic Path Commerce is no longer supported or maintained. To upgrade to the latest version, contact your Elastic Path representative.

RxJava

RxJava

The Helix programming model for Cortex uses a subset of RxJava classes as expected return types for all prototype operations. This document covers the specific parts of RxJava used by Cortex. More in-depth documentation can be found at reactivex.io.

Prototype Operations and RxJava

All prototype operation interfaces return one of the following RxJava types: Observable<T>, Single<T>, Maybe<T> or Completable.

Observable

Where a prototype class expects zero or more values to be returned, the generated operation interfaces return io.reactivex.Observable.

To return an Observable from a collection:

  1. Observable.just(someList);

Copy

In the event of zero values being returned, an empty Observable can be returned:

  1. Observable.empty();

Copy

Observable can also return an error:

  1. Observable.error(new MyException());

Copy

Single

In cases where a prototype class expects a single return value, the generated operation interfaces return io.reactivex.Single. Single can be used to express that either a value has been emitted or that the operation has failed.

To express a value:

  1. Single.just("foo");

Copy

To express a failure:

  1. Single.error(new MyException());

Copy

Note that in contrast to Observable<T>, there is no concept of an empty return value. Single must either return a value or an error.

Maybe

In cases where a prototype class expects an optional single return value, the generated operation interfaces return io.reactivex.Maybe. Maybe can be used to express that a value has been emitted, the value is empty, or that the operation has failed.

To express a value:

  1. Maybe.just("foo");

Copy

In the event of an empty value, an empty Maybe can be returned:

  1. Maybe.empty();

Copy

To express a failure:

  1. Maybe.error(new MyException());

Copy

Completable

In cases where a prototype class is not interested in a return value of generated operation interfaces, io.reactivex.Completable is returned. Completable indicates whether an operation was successful or it failed.

To express a successful computation:

  1. Completable.complete();

Copy

To express a failure:

  1. Completable.error(new MyException());

Copy

Operation Failures

The only runtime exceptions that prototype classes understand are ResourceOperationFailure exceptions. These should be used.

For example, to return a NOT_FOUND failure:

  1. return Observable.error(ResourceOperationFailure.notFound());

Copy

Rx Repositories

In some Helix Pattern Library examples, you'll see repositories that return RxJava Observable, Single, Maybe or Completable types. While this is not necessary, returning these allows prototype implementations to make use of the various Rx combinators.

For example:

  1. @Override
  2. public Single<ThingEntity> onRead() {
  3. return repository
  4. .retrieveThing(thingId.getValue())
  5. .map(thing -> ThingEntity.builder()
  6. .withName(thing.getName())
  7. .withDescription(thing.getDescription())
  8. .build());
  9. }

Copy

In the preceding example, the repository returns a Single<Thing> which allows use of the map operator to convert the returned value into a ThingEntity.

The same example, but with a "regular" return type:

  1. @Override
  2. public Single<ThingEntity> onRead() {
  3. Thing thing = repository.retrieveThing(thingId.getValue());
  4. ThingEntity thingEntity = ThingEntity.builder()
  5. .withName(thing.getName())
  6. .withDescription(thing.getDescription())
  7. .build();
  8. return Single.just(thingEntity);
  9. }

Copy

In addition to being able to use Rx combinators, maintaining the "Observable chain" makes handling and propagating errors fairly simple, without need for try/catch blocks.

RxJava Unit Testing

Unit testing of RxJava Observable types is straightforward. The RxJava test() method returns a TestObserver<T> object, which supports a wide range of xunit type test assertions.

Here is an example of testing the Observable<String> getWishlistIds(...) repository method:

  1. repository.getWishlistIds("customer-id", STORE_CODE)
  2. .test()
  3. .assertNoErrors()
  4. .assertValueCount(1)
  5. .assertValue(GOOD_WISH_LIST_GUID);

Copy

Note: The TestObserver can be used with all observable types: Observable, Single, Maybe and Completable.

Debugging via Stack Traces

To debug RxJava code via stack traces, use the RxJava2Extensions plugin, available from RxJava's github.

RxJava code is executed upon subscription to an Observable type. Because of this, when a failure occurs, an exception stack trace may not tell which Rx operation failed. The RxJavaAssemblyTracking class captures the stack trace when Observable, Single, Maybe and Completable operators are instantiated at assembly-time. Whenever an error is signaled via onError(), this assembly-time stack trace is attached as the last cause of that exception.

Example: Enabling Assembly Tracking for the Entire Application

Note:

Enabling assembly tracking has severe performance impact, and it should never be enabled in production. It should only be used for debugging purposes in development.

Add the dependency for RxJava2Extensions:

  1. <dependency>
  2. <groupId>com.github.akarnokd</groupId>
  3. <artifactId>rxjava2-extensions</artifactId>
  4. <version>0.18.1</version>
  5. </dependency>

Copy

Enable assembly tracking:

  1. @Component
  2. public class RxAssemplyHookInitializer {
  3. @Activate
  4. public void initialize() {
  5. RxJavaAssemblyTracking.enable();
  6. }
  7. }

Copy

Once enabled, use the following snippet:

  1. Observable.empty().single()
  2. .subscribe(System.out::println, Throwable::printStackTrace);

Copy

Which will result in a stacktrace similar to the one shown below:

  1. java.lang.NoSuchElementException
  2. at rx.internal.operators.OnSubscribeSingle(OnSubscribeSingle.java:57)
  3. ...
  4. RxJavaAssemblyException: assembled
  5. at com.example.TrackingExample(TrackingExample:10)

Copy