Starting v3.0.1, Watson Developer Cloud Java SDK has introduced Reactive API that allows developers to compose and combine asynchronous congitive operations in a reactive fashion.

Each Service(TextToSpeech, LanguageTranslator, SpeechToText…) API has .rx() method that returns CompletableFuture. CompletableFuture provides powerful APIs to chain multiple asynchronous calls.

Let us consider a scenario and try to achive that using Reactive Watson API. Let’s say we want to achieve following use cases in our cognitive system:

  • text translation from English to French
  • speech synthesis of tranlsated text

For this, we will use LanguageTranslator and TextToSpeech API from Watson Developer Cloud Java SDK. We will leverage Reactive API to chain two asynchronous operations (translation and speech synthesis) and gather result.

Assuming that the bluemix account is already created and both Language Translator and Text to Speech services are available for use, let’s jump into the code section that shows how the two asynchronous operations are chained together.

 1     ...
 2 
 3     LanguageTranslator translator = new LanguageTranslator();
 4     translator.setUsernameAndPassword("<username>", "<password>");
 5 
 6     TextToSpeech tts = new TextToSpeech();
 7     tts.setUsernameAndPassword("<username>", "<password>");
 8 
 9     translator
10       .translate("hello", Language.ENGLISH, Language.FRENCH)
11       .rx()
12       .thenApply(translationResult -> translationResult.getFirstTranslation())
13       .thenApply(translation -> tts.synthesize(translation, Voice.FR_RENEE, AudioFormat.WAV).rx())
14       .thenAccept(App::processSpeechSynthesis);
15 
16     ...

Although it may look intimidating at first glance but it actually makes more sense once we get to know the Reactive API and what it does. On line 11, we are grabbing the CompletableFuture for the translation operation and we are chaining it with the text to speech API on line 13. Once the translation result is available, it is passed to the text to speech operation. thenApply is used to pass in the result from one asynchronous operation to another operation. On line 11, we are grabbing the translated text from the result object of the translation and passing it to the text to speech operation. Finally, we use thenAccept to mark an end of chaining which tells that we are now ready to consume the result.

On line 14, App::processSpeechSynthesis is a reference to the method that handles the result of speech synthesis. Method reference is a feature introduced in Java 8 that allows us to reference constructors or methods without executing them. processSpeechSynthesis is a static method that plays the audio data received from text to speech service.

1     private static void processSpeechSynthesis(CompletableFuture<InputStream> result) {
2         ...
3         InputStream stream = result.get();
4         stream = WaveUtils.reWriteWaveHeader(stream);
5         AudioStream audioStream = new AudioStream(stream);
6         AudioPlayer.player.start(audioStream);
7         ...
8     }

The asynchronous pattern discussed above is just a tip of iceberg and it can be applied in many scenarios to build asynchronous system.

The code sample is available in Github.