While callbacks are a simple form of asynchronous event notification, they can easily render asynchronous code complicated. Let us see why on a real example.
We are going to take the example of a edge service to illustrate composing asynchronous operations with different asynchronous programming models. A edge service is also frequently called an "API gateway". It is a service that serves as a facade to other services, so that a requester has to deal with just one service interface rather than having to talk to each services. It may also perform other tasks such as data transformation and interacting with other services, so it is not just about conveniently aggregating data from multiple services.
Let us get back to the heat sensor verticles that we had used in a previous chapter. Suppose that we have several heat sensors, and that we want to expose an API to fetch and aggregate all of the sensors heat data. This is a very simple yet effective example of edge service, as it abstracts the need for a requester to know and contact all sensors. To make things even more interesting, we will also have a snapshot service that captures and logs the sensor values before they are returned to the requester. The whole scenario is illustrated in figure 5.1.
The requester issues a request to the edge service, which in turn fetches temperature data from the sensor services. Each sensor exposes a HTTP/JSON API, and the edge service aggregates all responses in a larger JSON document. This document is then sent to the snapshot service, before sending it back to the requester.
The interactions are summarized in figure 5.2.
This example allows us to reason about:
We could deploy our "heat sensors" as multiple independent processes each exposing a HTTP API. To simplify our example, we are simply going to deploy them within the same process, albeit with HTTP servers listening on different TCP ports.
The HeatSensor class below is a simple adaptation of the one we used earlier. Listing 5.1 gives the preamble of the class, directly ported from the code we had written.
public class HeatSensor extends AbstractVerticle { private final Random random = new Random(); private final String id = UUID.randomUUID().toString(); #1 private double temp = 21.0; private void scheduleNextUpdate() { vertx.setTimer(random.nextInt(5000) + 1000, this::update); } private void update(long tid) { temp = temp + (delta() / 10); scheduleNextUpdate(); } private double delta() { if (random.nextInt() > 0) { return random.nextGaussian(); } else { return -random.nextGaussian(); } } // (...)
The code keeps the same logic of upating the temperature by a random amount, with a random delay between 1 and 6 seconds. Listing 5.2 gives us the code added to expose a HTTP API.
@Override public void start() { vertx.createHttpServer() .requestHandler(this::handleRequest) .listen(config().getInteger("http.port", 3000)); #1 scheduleNextUpdate(); } private void handleRequest(HttpServerRequest req) { JsonObject data = new JsonObject() .put("id", id) .put("temp", temp); req.response() .putHeader("Content-Type", "application/json") .end(data.encode()); }
It is a very straightforward usage of the Vert.x HTTP server, with the HTTP port being passed by configuration. The response is encoded in JSON.
The snapshot service exposes a HTTP server as well, and is given in listing 5.3.
public class SnapshotService extends AbstractVerticle { private final Logger logger = LoggerFactory.getLogger(SnapshotService.class); @Override public void start() { vertx.createHttpServer() .requestHandler(req -> { if (badRequest(req)) { req.response().setStatusCode(400).end(); } req.bodyHandler(buffer -> { #1 logger.info("Lastest temperatures: {}", buffer.toJsonObject().encodePrettily()); req.response().end(); }); }) .listen(config().getInteger("http.port", 4000)); } private boolean badRequest(HttpServerRequest req) { return !req.method().equals(HttpMethod.POST) || !"application/json".equals(req.getHeader("Content-Type")); } }
The HTTP request handler expects a HTTP POST request, extracts the body using a body handler, and logs the received data.
With these 2 verticles defined, the fun can now begin and we can look into making our edge service.
We are first going to implement the edge service using callbacks, as we have been doing since the beginning of this book.
The dependencies we need for our project are Vert.x Core, Vert.x Web Client (to simplify making HTTP requests), and Logback. Listing 5.4 gives the dependencies for a Gradle build. The artefact are exactly the same when using Maven or any other compatible build tool.
dependencies { implementation("io.vertx:vertx-core:${vertxVersion}") #1 implementation("io.vertx:vertx-web-client:${vertxVersion}") implementation("ch.qos.logback:logback-classic:1.2.3") }
Let us start with the preamble of the CollectorService verticle class implementation in listing 5.5.
public class CollectorService extends AbstractVerticle { private final Logger logger = LoggerFactory.getLogger(CollectorService.class); private WebClient webClient; @Override public void start() { webClient = WebClient.create(vertx); #1 vertx.createHttpServer() .requestHandler(this::handleRequest) .listen(8080); } // (...) }
The start method first creates a WebClient instance then starts a HTTP server on port 8080. The web client class comes from the vertx-web-client module and greatly simplifies making HTTP requests compared to the HTTP client in the Vert.x core APIs. It especially simplifies HTTP body handling, by providing simplified conversion to JSON, plain text and more general-purpose buffers.
The HTTP requests handler is method handleRequest given in listing 5.6.
private void handleRequest(HttpServerRequest request) { List<JsonObject> responses = new ArrayList<>(); #1 AtomicInteger counter = new AtomicInteger(0); #2 for (int i = 0; i < 3; i++) { webClient .get(3000 + i, "localhost", "/") #3 .expect(ResponsePredicate.SC_SUCCESS) #4 .as(BodyCodec.jsonObject()) #5 .send(ar -> { if (ar.succeeded()) { responses.add(ar.result().body()); } else { logger.error("Sensor down?", ar.cause()); } if (counter.incrementAndGet() == 3) { #6 JsonObject data = new JsonObject() .put("data", new JsonArray(responses)); sendToSnapshot(request, data); } }); } }
The method shows how easy the web client API is for performing HTTP requests. However the main difficulty lies in the parallel HTTP requests coordination. We need a loop to issue the requests. Since they are asynchronous, we also need to keep track of the number of received responses, as well as the response values. This is done by having a list of responses, and using a counter of responses. Note that we use an AtomicInteger here not because of concurrency, but rather because we need an object to increment an integer from the callbacks.
Once all responses have been received, we can move to the next operation which is sending the data to the snapshot service given in listing 5.7.
private void sendToSnapshot(HttpServerRequest request, JsonObject data) { webClient .post(4000, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .sendJsonObject(data, ar -> { if (ar.succeeded()) { sendResponse(request, data); } else { logger.error("Snapshot down?", ar.cause()); request.response().setStatusCode(500).end(); #1 } }); }
This method implementation is simply about using the web client to issue a HTTP POST request. Upon success the code moves to the sendResponse method to end the HTTP request. The code is given in listing 5.8.
private void sendResponse(HttpServerRequest request, JsonObject data) { request.response() .putHeader("Content-Type", "application/json") .end(data.encode()); #1 }
To run the edge service, we first need to deploy verticles as in listing 5.9.
Vertx vertx = Vertx.vertx(); vertx.deployVerticle("chapter5.sensor.HeatSensor", new DeploymentOptions().setConfig(new JsonObject() .put("http.port", 3000))); #1 vertx.deployVerticle("chapter5.sensor.HeatSensor", new DeploymentOptions().setConfig(new JsonObject() .put("http.port", 3001))); vertx.deployVerticle("chapter5.sensor.HeatSensor", new DeploymentOptions().setConfig(new JsonObject() .put("http.port", 3002))); vertx.deployVerticle("chapter5.snapshot.SnapshotService"); vertx.deployVerticle("chapter5.callbacks.CollectorService");
We can issue HTTP requests to test the service with HTTPie, as given in listing 5.10:
$ http :8080 #1 HTTP/1.1 200 OK Content-Type: application/json content-length: 224 { "data": [ { "id": "66e310a6-9068-4552-b4aa-6130b3e17cb6", "temp": 21.118902894421108 }, { "id": "3709b24b-cef2-4341-b64a-af68b11e2c0d", "temp": 20.96576368750857 }, { "id": "79f9fa27-b341-4ce5-a335-03caef6e8935", "temp": 21.01792006568459 } ] }
On the server side, we can check in the logs that the snapshot service outputs some logs as in listing 5.11:
15:10:25.576 SnapshotService - Lastest temperatures: { #1 "data" : [ { "id" : "66e310a6-9068-4552-b4aa-6130b3e17cb6", "temp" : 21.118902894421108 }, { "id" : "3709b24b-cef2-4341-b64a-af68b11e2c0d", "temp" : 20.96576368750857 }, { "id" : "79f9fa27-b341-4ce5-a335-03caef6e8935", "temp" : 21.01792006568459 } ] }
Many people will scream "callback-hell!" when discussing callbacks.  Error-handling is especially more difficult with nested callbacks.
While this is true, the callback-hell can be easily mitigated using 1 method for each asynchronous operation callback as we did with methods handleRequest, sendToSnapshot and sendResponse. Each method does exactly one thing and we avoid nesting of callbacks.
Listing 5.12 shows the equivalent code as above, but compacted as a single piece with nested callback.
List<JsonObject> responses = new ArrayList<>(); AtomicInteger counter = new AtomicInteger(0); for (int i = 0; i < 3; i++) { webClient .get(3000 + i, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .as(BodyCodec.jsonObject()) .send(ar -> { #1 if (ar.succeeded()) { responses.add(ar.result().body()); } else { logger.error("Sensor down?", ar.cause()); } if (counter.incrementAndGet() == 3) { #2 JsonObject data = new JsonObject() .put("data", new JsonArray(responses)); webClient .post(4000, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .sendJsonObject(data, ar1 -> { #3 if (ar1.succeeded()) { #4 request.response() .putHeader("Content-Type", "application/json") .end(data.encode()); } else { logger.error("Snapshot down?", ar1.cause()); request.response().setStatusCode(500).end(); } }); } }); }
Nested callbacks surely do not render the code more readable, yet I argue that the real issue lies in the fact that the functional code is entangled with asynchronous coordination code. You need to decipher from the loop, callbacks and branching that 3 HTTP requests are being made in parallel, that their results are being assembled, sent to a third-party service and then given back as a response.
Callbacks aren’t perfect yet a bit of discipline keeps the code readable, especially when all you have is sequential composition of asynchronous operations, like sendToSnapshot passing the work to sendResponse.
Let us now see other asynchronous programming models that can be more interesting than callbacks.
You have already been exposed to Vert.x future and promises due to the signature of the verticle start methods. You may also have been exposed to them in other languages like JavaScript. Let us now explore more of this model, and how they are an interesting asynchronous operation composition primitive with Vert.x.
Vert.x implements a model of future and promises which is in line with the original research results from Barbara Liskov and Liuba Shrira [Liskov88]. They introduced promises as a language abstraction for composing asynchronous remote procedure calls.
A promise holds the value of some computation for which there is no value right now. A promise is eventually completed with a result value or an error. In the context of asynchronous I/O, a promise is a natural fit for holding the result of an asynchronous operation. In turn a future allows to read a value which will be eventually available from a promise.
To summarize: a promise is used to write an eventual value, and a future is used to read it when it is available. Let us now see how it works in Vert.x.
 As an example, imagine that you want to notify of a result not now but in 5 seconds. In Vert.x you would use a timer for that, and a promise would be used to hold the result, as shown in listing 5.13.
Here the asynchronous operation is a timer of 5 seconds, after which the promise is completed. Depending of whether the current time is odd or even, the promise completes with a value or fails with an exception.
This is great, but how do we actually get the value from the promise?
The code that wants to react when the result is available needs a future object. A Vert.x future is created from a promise, then passed on to the code that wants to read the value, as given in listing 5.14 which is the rest of listing 5.13.
The Future interface defines 2 methods onSuccess and onFailure for handling values and errors. When we run the corresponding code, we see that either "Ok!" or "Bad luck…" is printed after 5 seconds.
We can perform more advanced asynchronous operations with futures, as given in listing 5.15.
The recover operation is called when the promise is failed, and it is used to replace the error by another value. You can see recover as the equivalent of a catch block in Java where you can handle an error. Here we simply provide a recovery value using a succeeded future, but you can also report a failed future in more advanced cases when there is nothing you can do to recover. The map operation transforms a value using a function, while flatMap composes with another asynchronous operation.
You can think of flatMap as "and then": here the operation takes the string value and prepends it with ">>>" after 3 seconds. We also see the typical promise / future pattern where we first create a promise, do an asynchronous operation that eventually completes the promise, and return a future so the value can be consumed by another piece of code.
Vert.x 4 brings Vert.x futures to the core APIs alongside callbacks. While callbacks remain the canonical model, most APIs are available with variants that return a Future.
This means that given a method void doThis(Handler<AsyncResult<T>>) then there exists a variant of the form Future<T> doThis(). A good example is that of listing 5.16 where we start a HTTP server.
The listen method that we had seen in earlier examples took a callback, here it returns a Future<HttpServer>. We can then chain calls to onFailure and onSuccess to define what to do when the server starts, or when an error occurs.
You can use the new promise / future interfaces starting from Vert.x 3.8, but the future-based APIs are only available in Vert.x 4.
Vert.x futures are also inter-operable with the CompletionStage interface of the java.util.concurrent package in the JDK. The CompletionStage interface represents a step in an asynchronous operation, so you can think of it as being a future, especially as there is a class called CompletableFuture that implements CompletionStage. For instance the HTTP client API in Java 11 offers sendAsync methods that return CompletableFuture to make asynchronous HTTP requests.
The inter-operability between Vert.x futures and CompletionStage is useful when you need to interact with libraries that use CompletionStage in their APIs.
The Vert.x Future interface is not a subtype of CompletionStage. The Vert.x team had thought about it while preparing the roadmap for Vert.x 4, but we ultimately opted for our own interface definition since CompletionStage is more agnostic of the threading model. Indeed the "async"-suffixed methods provide variants where you can pass an executor like CompletionStage<Void> thenRunAsync(Runnable,Executor), while the variants without an executor parameter dispatch by default to a ForkJoinPool instance. These methods allow to step out of Vert.x event-loops or worker thread pools too easily, hence we chose to offer inter-operability and not use CompletionStage directly in the Vert.x APIs.
Listing 5.17 shows how to move from a Vert.x Future to a CompletionStage.
Here we convert the string result to upper-case, then prefix it with a string, and eventually whenComplete is being called. Note that this is a BiConsumer and you need to test which of the value or exception parameter is null to know wether the promise did complete successfully or not. It is also important to note that unless you call an "async" CompletionStage method then the calls are performed on a Vert.x thread.
Last but not least you can convert a CompletionStage to a Vert.x Future, as in listing 5.18.
CompletionStage
to a Vert.x futureCompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { #1 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "5 seconds have elapsed"; }); Future .fromCompletionStage(cf, vertx.getOrCreateContext()) #2 .onSuccess(System.out::println) .onFailure(Throwable::printStackTrace);
CompletableFuture implements CompletionStage, and supplyAsync dispatches a call to the default ForkJoinPool. A thread from that pool will be used, sleeping for 5 seconds before returning a string which will be the CompletableFuture result. The fromCompletionStage method converts to a Vert.x Future. The method has 2 variants: one with a Vert.x context to call Future methods like onSuccess on the context, and one where the calls will happen on whatever thread completed the provided CompletionStage instance.
Going back to the edge service example, we can make use of the Vert.x APIs that use Future. The verticle start method is that of listing 5.16.
First of all we define the fetchTemperature method in listing 5.19 to get the temperature from a service.
This method returns a future of a JsonObject, and to do so we use the future-returning variant of the WebClient send method, then map the result to extract just the JSON data. Temperatures are collected in the handleRequest method of listing 5.20.
private void handleRequest(HttpServerRequest request) { CompositeFuture.all( #1 fetchTemperature(3000), #2 fetchTemperature(3001), fetchTemperature(3002)) .flatMap(this::sendToSnapshot) #3 .onSuccess(data -> request.response() #4 .putHeader("Content-Type", "application/json") .end(data.encode())) .onFailure(err -> { #5 logger.error("Something went wrong", err); request.response().setStatusCode(500).end(); }); }
You can use CompositeFuture to make a future out of several ones. The all static method results in a future that is completed when all futures are completed, and fails when any future has failed. There also exist any and join that have different semantics.
Once all temperatures have been successfully received, the call to flatMap sends data to the snapshot service, which is an asynchronous operation. The code for the sendToSnapshot method is given in listing 5.21.
private Future<JsonObject> sendToSnapshot(CompositeFuture temps) { List<JsonObject> tempData = temps.list(); JsonObject data = new JsonObject() .put("data", new JsonArray() .add(tempData.get(0)) .add(tempData.get(1)) .add(tempData.get(2))); return webClient .post(4000, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .sendJson(data) #1 .map(response -> data); }
The code is similar to that of fetchTemperature as we use a method of WebClient that returns a Future. The code of the main method that deploys verticles is the same as the callbacks variant, except that we deploy a different CollectorService verticle:
// (...) vertx.deployVerticle("chapter5.future.CollectorService");
Let us now move on to reactive extension, another asynchronous programming model.
Reactive extensions are an elaborated form of the observable design pattern [DesignPatterns]. They were first popularized by Erik Meijer in the Microsoft .Net ecosystem. Modern applications are increasingly composed of asynchronous event streams [Meijer12]. This is true not just on the server, but also in web, desktop and mobile clients. Indeed, we can think of graphical user interface events as a stream of events that an application has to respond to.
Reactive extensions are about 3 things:
The ReactiveX initiative offers a common API and implementations in many languages, both for backend and frontend projects [RX]. The RxJS project offers reactive extensions for JavaScript applications in the browser, while a project like RxJava offers a general-purpose reactive extensions implementation for the Java ecosystem.
Vert.x offers bindings for RxJava versions 1 and 2. It is recommended using version 2 since it supports back-pressure, while version 1 does not.
A book like [RxJavaAndroid] is a solid resource for learning RxJava. Let us however explore the basics and give you the intuition of what RxJava is, and how it nicely integrates with Vert.x
First of all, RxJava 2 offers 5 different types of observable sources, given in table 5.1.
| Type | Description | Example |
|
|
A stream of events of type |
Timer events, observable source where we cannot apply back-pressure like GUI events. |
|
|
A stream of events of type |
Network data, filesystem inputs. |
|
|
A source that emits exactly one event of type |
Fetching an entry from a data store by key. |
|
|
A source that may emit one event of type |
Fetching an entry from a data store by key, but the key may not exist. |
|
|
A source that notifies of some action having completed, but no value is being given. |
Deleting files. |
You may sometimes read about "hot" and "cold" sources. A hot source is a source where events are being emitted no matter if there are subscribers or not. A cold source is a source where events only start being emitted after the first subscription. A periodic timer is a hot source, while a file to read is a cold source. With a cold source you can get all events, while with a hot source you will only get those emitted after you have subscribed.
Let us start with a simple example in listing 5.22, and illustrated in figure 5.3.
Observable.just(1, 2, 3) #1 .map(Object::toString) #2 .map(s -> "@" + s) #3 .subscribe(System.out::println); #4
Running it yields the following console output:
@1 @2 @3
This example creates an observable of 3 integers. The just factory method creates an Observable<Integer> source. We then use 2 map operators to transform the stream. The first one converts from an Observable<Integer> to an Observable<String>. The second one prepends the "@" character to each item. Finally, subscribe performs a subscription where for each item, System.out.println is being called.
Sources may emit errors, in which case the subscriber can be notified. Let us consider the observable of listing 5.23.
The observable of string values will just emit one error. The map operator will never be called, since it operates only on values, not errors. We can see that subscribe now has 2 parameter: the second one is the callback to process errors. In this case we just print the stack trace but in, say, networked applications we would do error recovery.
Using the just factory method is great in examples and tests, but of course in real-world scenarios you will want to adapt a more complex source to produce events to an RxJava observable type. To do that, there is a general-purpose Publisher interface that you can implement to emit items to subscribers using the fromPublisher method (instead of just). There are also adapter methods for JDK futures, iterable objects and also generating items from a JDK callable object.
The previous example did not show the full life-cycle of an observable. Once a subscription has been made, 0 or many items are emitted. Then, the stream terminates with either an error, or a notification that it has completed.
Let us look at the more elaborated example in listing 5.24.
Observable .just("--", "this", "is", "--", "a", "sequence", "of", "items", "!") .doOnSubscribe(d -> System.out.println("Subscribed!")) #1 .delay(5, TimeUnit.SECONDS) #2 .filter(s -> !s.startsWith("--")) .doOnNext(System.out::println) #3 .map(String::toUpperCase) .buffer(2) #4 .subscribe( System.out::println, Throwable::printStackTrace, () -> System.out.println(">>> Done")); #5
Running it gives the following output:
Subscribed! #1 doOnNext: this doOnNext: is next: [THIS, IS] doOnNext: a doOnNext: sequence next: [A, SEQUENCE] doOnNext: of doOnNext: items next: [OF, ITEMS] doOnNext: ! next: [!] ~Done~
This example shows us the form of subscribe where all events can be handled: an event, an error and the completion of the stream. The example also shows further operators:
doOnSubscribe
ncy doOnNext
tks nsiatco (rpwj tniptolae zjqk-seftefc) rzpr ssn yk eregtrdig zs estim cacb golna yvr rtseam,delay
oslwla dilngeya vwun vstnee arstt rx oq diemtte ruetrhf npwv orp emarts,buffer
pousrg esevtn (jnkr istsl), ak kktp wk orb tvsene nj ispra.There is of course more to RxJava than this section, but what we briefly covered should be enough to dive into the Vert.x and RxJava integration.
The RxJava integration in Vert.x is available from the vertx-rx-java2 module. In Gradle (and similarly in Maven), the dependency can be added as:
implementation("io.vertx:vertx-rx-java2:version")
All APIs from the projects in the official Vert.x stack have RxJava support. The RxJava APIs are automatically generated from those of the core APIs. There are several idiomatic conversion rules to RxJava APIs, but as a simple example when we have:
void foo(String s, Handler<AsyncResult<String>> callback)
then the translation to RxJava is:
Single<String> foo(String s)
The RxJava APIs are in sub-packages of io.vertx.reactivex. For instance the RxJava version of AbstractVerticle is io.vertx.reactivex.core.AbstractVerticle.
Let us now see an example verticle using the RxJava APIs in listing 5.26.
public class VertxIntro extends AbstractVerticle { @Override public Completable rxStart() { #1 Observable .interval(1, TimeUnit.SECONDS, RxHelper.scheduler(vertx)) #2 .subscribe(n -> System.out.println("tick")); return vertx.createHttpServer() .requestHandler(r -> r.response().end("Ok")) .rxListen(8080) #3 .ignoreElement(); #4 } }
This example opens a classic HTTP server that replies Ok to any request. The interesting part is that the RxJava variant of AbstractVerticle has a rxStart (and rxStop) method that notifies of deployment success. In our case the verticle has been successfully deployed when the HTTP server has starter, so we return a Completable object. We can check that methods prefixed with rx correspond to generated methods for supporting RxJava. If you inspect the RxJava APIs, you will note that the original methods (including callbacks) are still present.
The other interesting part of the example is the observable that emits events every second. It behaves essentially as a Vert.x timer would be. There are several operator methods in the RxJava APIs that accept a scheduler object, because they need to defer asynchronous tasks. By default they call back from an internal worker thread pool that they manage, which breaks the Vert.x threading model assumptions. We can always pass a Vert.x scheduler to ensure that events are still being called back on the original context event loop.
We can finally go back to our edge service example. We can now rewrite the CollectorService verticle class with RxJava.
To begin with, we update the imports to use the io.vertx.reactivex.* packages. Since the verticle starts a HTTP server, we can take advantage of rxStart as shown in listing 5.27.
@Override public Completable rxStart() { webClient = WebClient.create(vertx); return vertx.createHttpServer() .requestHandler(this::handleRequest) .rxListen(8080) #1 .ignoreElement(); #2 }
The next step is to have a method for fetching temperatures in parallel, and then assemble the responses as a JSON object. Just like the callbacks version, we can have a method to fetch a single temperature. The code is given in listing 5.28.
Again, the difference to the callbacks versions is that we use rxSend (that returns a Single) instead of send (that uses a callback). Listing 5.29 gives the method that composes parallel asynchronous HTTP requests and assembles a JSON object based on the responses.
private Single<JsonObject> collectTemperatures() { Single<HttpResponse<JsonObject>> r1 = fetchTemperature(3000); Single<HttpResponse<JsonObject>> r2 = fetchTemperature(3001); Single<HttpResponse<JsonObject>> r3 = fetchTemperature(3002); return Single.zip(r1, r2, r3, (j1, j2, j3) -> { #1 JsonArray array = new JsonArray() .add(j1.body()) .add(j2.body()) .add(j3.body()); return new JsonObject().put("data", array); #2 }); }
By using fetchTemperature to fetch individual responses, we obtain Single objects that observe individual HTTP responses. To compose the results, we use the zip operator that takes severable sources, and composes a result as another Single object. When all HTTP responses are available, the zip operator passes the values to a function that must produce a value (of any type). The returned value is then the Single object that the zip operator emits. Here we build a JSON array using the HTTP response bodies that the Vert.x web client has converted to JSON for us, then we wrap the array in a JSON object.
Note that zip has many overloaded definitions with varying number of parameters to cope with 2 sources, 3 sources, etc. When the code needs to handle an undefined number of sources, there is a variant taking a list of sources, and the function passed to zip accepts a list of values.
This leads us to the definition of the HTTP request handling method, that collects the temperatures, posts to the snapshot service, and then responds to the requester. The code is given in listing 5.30.
private void handleRequest(HttpServerRequest request) { Single<JsonObject> data = collectTemperatures(); sendToSnapshot(data).subscribe(json -> { #1 request.response() .putHeader("Content-Type", "application/json") .end(json.encode()); }, err -> { #2 logger.error("Something went wrong", err); request.response().setStatusCode(500).end(); }); }
This method also performs the subscription: upon success JSON data is returned to the requester, and on failure a HTTP 500 error is returned. It is important to note that the subscription triggers the HTTP requests to the sensor services, then to the snapshot service, and so on. Until a subscription is being made, RxJava observable pipelines are just "recipes" for processing events.
The last missing part is the method that sends data to the snapshot service, given in listing 5.31.
This method introduces the flatMap operator which is well-known to the functional programming enthusiasts. Do not worry if flatMap sounds cryptic to you: in the case of composing sequential asynchronous operations you can just read "flatmap" as "and then".
Since data emits a JSON object, the flatMap operator allows to issue a HTTP request with the web client once that JSON object has been emitted. We need another (nested) flatMap after the HTTP request to the snapshot service has succeeded. Indeed, rxSendJsonObject gives a single observable that emits a HTTP response. However, we need the JSON object because it has to be returned to the requester after sending to the snapshot service has succeeded, so the second flatMap allows to do that and re-inject it in the pipeline. This is a very common pattern with RxJava.
Running the RxJava version of the edge service is no different from the callbacks version. All we need is changing the deployment of the CollectorService to:
vertx.deployVerticle("chapter5.reactivex.CollectorService");
Interacting with the service yields the same results as with the callback version.
"FlatMap" comes from the "flatten" and "map" operators. To better understand how it works, let us illustrate flatMap with JavaScript arrays (you can test using node or directly from a web browser console).
With let a = [1, 2, 3], a is an array with values 1, 2 and 3. Now suppose that for each value, we want to get the value multiplied by 10 and 100. With map, we could write let b = a.map(x ⇒ [x * 10, x * 100]), which gives us an array of arrays: [ [ 10, 100 ], [ 20, 200 ], [ 30, 300 ] ].
This is not very convenient if we just want the values rather than nested arrays, so we can flatten b: b.flat() and then it gives us [ 10, 100, 20, 200, 30, 300 ]. You can get the same result directly with a.flatMap(x ⇒ [x * 10, x * 100]).
This translates directly to other operations like HTTP client requests or database calls, as flatMap avoids having nested observables of observables.
The last asynchronous programming model to explore is that of coroutines in the Kotlin programming language [KotlinInAction]. Stable support for coroutines is a recent addition to the language that is very well explained in [KotlinCoroutines].
Coroutines are interesting to explore, as in many cases they allow to write asynchronous code that looks like regular non-asynchronous code. Also, Kotlin has a solid implementation of coroutines, it is very easy to understand for Java developers, and since we said that Vert.x was polyglot this book had to show Vert.x without Java at some point!
The term coroutines first appeared in a paper from Melvin Conway in 1963 about the design of a COBOL compiler [Conway63]. Many languages have support for coroutines, or some form of coroutines: Python (generators), C# (async / await operators), Go (goroutines) and more. Implementations for Java using bytecode instrumentation exist, and a future version of Java will have support for coroutines thanks to Project Loom [Loom].
A coroutine can have its execution suspended and resumed at a later point in time. It can be seen as a function with multiple entry and exit points, and for which its execution stack can be resumed. Coroutines fit well with an asynchronous model, as they can be suspended when the result of an asynchronous operation is needed, and resumed when it is available.
To make things more concrete, let us look at using coroutines in Kotlin. First, let us consider the code in listing 5.32.
import kotlinx.coroutines.* suspend fun hello(): String { #1 delay(1000) #2 return "Hello!" } fun main() { runBlocking { #3 println(hello()) } }
The call to delay does not block the caller thread because that method can be suspended. The method is being called again when the time has elapsed, and resumes executing at the next line, which returns a string. In a callback world the delay function would have taken a callback parameter, which would have had to pass the returned string to the caller, probably using another callback.
Here is a more elaborated example in listing 5.33.
In this example job1 is created using launch, which executes some code in parallel. It waits for 500ms. The same applies to job2, except that async is for code blocks that return a value. It computes the Fibonacci value of 42, which takes some time. The join and await method on the jobs allow to wait for these jobs to complete. Last but not least the main function is wrapped in a runBlocking call. This is because suspending methods are being called, so the execution must wait for all coroutines to complete.
While we only scratched the surface of Kotlin and coroutines, this should be enough to look at the Vert.x integration. To dive more into Kotlin, you may also read [Saumont19].
Vert.x offers a first-class support for Kotlin coroutines.
To use them in a Gradle project, you need the dependencies and configuration of listing 5.34.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "kotlinVersion" #1 } dependencies { // (...) implementation("io.vertx:vertx-lang-kotlin:version") implementation("io.vertx:vertx-lang-kotlin-coroutines:version") #2 implementation(kotlin("stdlib-jdk8")) #3 } val compileKotlin: KotlinCompile by tasks compileKotlin.kotlinOptions.jvmTarget = "1.8" #4 // (...)
Again, the coroutine bindings are being generated from the callback APIs. The convention is that for any method having a callback, a Kotlin suspending method is being generated with the suffix Async. Given:
void foo(String s, Handler<AsyncResult<String>> callback)
then the following method exists in the Kotlin coroutines binding:
suspend fun String fooAsync(String s)
There exists a verticle base class called io.vertx.kotlin.coroutines.CoroutineVerticle where the start and stop methods are suspending, so you can use coroutines directly from them. By using CoroutineVerticle you also get to execute coroutines on the verticle event loop thread rather than on threads of a worker pool like the default Kotlin coroutines do.
If you write Vert.x code in Kotlin, you can also directly use RxJava from Kotlin. There is also a helper RxKotlin library that makes some RxJava APIs even more idiomatic from Kotlin.
Let us see the implementation of the edge service using Kotlin coroutines.
The preamble is given in listing 5.35.
class CollectorService : CoroutineVerticle() { private val logger = LoggerFactory.getLogger(CollectorService::class.java) private lateinit var webClient: WebClient #1 override suspend fun start() { webClient = WebClient.create(vertx) vertx.createHttpServer() .requestHandler(this::handleRequest) .listenAwait(8080) #2 } // (...) }
Compared to the other implementations there is not much difference except that the start method is suspending, and starting the HTTP server is done using listenAwait. Since that method call is suspending, the execution resumes when the HTTP server is running, and the method returns the HTTP server instance, which we simply ignore here.
Listing 5.36 shows the code for the fetchTemperature and sendToSnapshot methods adapted to coroutines.
private suspend fun fetchTemperature(port: Int): JsonObject { return webClient .get(port, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .`as`(BodyCodec.jsonObject()) #1 .sendAwait() .body() } private suspend fun sendToSnapshot(json: JsonObject) { webClient .post(4000, "localhost", "/") .expect(ResponsePredicate.SC_SUCCESS) .sendJsonAwait(json) }
Both methods now look like more classical imperative code. fetchTemperature returns a value (a JSON object), albeit asynchronously as the execution in the method is suspended when calling sendAwait.
The illusion of "asynchronous operations that do not look like asynchronous operations" is even more apparent in listing 5.37 that contains the core logic of the edge service.
private fun handleRequest(request: HttpServerRequest) { launch { try { val t1 = async { fetchTemperature(3000) } #1 val t2 = async { fetchTemperature(3001) } val t3 = async { fetchTemperature(3002) } val array = Json.array(t1.await(), t2.await(), t3.await()) #2 val json = json { obj("data" to array) } #3 sendToSnapshot(json) request.response() .putHeader("Content-Type", "application/json") .end(json.encode()) } catch (err: Throwable) { #4 logger.error("Something went wrong", err) request.response().setStatusCode(500).end() } } }
This code expresses very naturally that temperatures are being fetched asynchronously, their values collected in a JSON object, the snapshot service being called, and the result being eventually sent to the requester. Still, there are many suspension points with asynchronous operations. Also the error management is a familiar try / catch structure.
You may have noticed the launch function call to wrap the whole method code. This is because while the start method is suspending, the HTTP request handler is not a suspending function type, and it is going to be called outside of a Kotlin coroutine context. Calling launch ensures a coroutine context is being created, so suspending methods can be called. Also, the coroutine is automatically attached to a context that ensures events run on the verticle event-loop thread (thanks to the internals of CoroutineVerticle).
Coroutines are no magic, and the implementation requires special compiler and runtime library support. The intuition is that a suspending function is split as many functions. The split points are the suspending function calls, where the rest of the function ends up in another function (called a "continuation"). There is then a finite state machine to know what split function to call when the suspended function shall resume. The whole proposal and design proposal for Kotlin coroutines is at [KeepKtCoroutines].
We just covered 3 different asynchronous programming models in this chapter that are in general better than callbacks. There is no definite answer about what model you should be using to write Vert.x applications. Opting for one model or the other depends essentially of what you are trying to achieve.
This is the great thing about Vert.x: you can write a verticle with RxJava because it makes the code straightforward for the functional requirements of that verticle, and use Kotlin coroutines for another verticle. You can mix and match models within the same application.
Future and promises are a simple and effective model to compose asynchronous operations. They are built-in into the Vert.x core APIs starting from version 4, and they offer the essential tools to deal with asynchronous results: transforming values (map), recovering from errors (recover / otherwise), chaining (flatMap) and composition (CompositeFuture). They also provide inter-operability with CompletionStage from the JDK.
RxJava allows to reason about streams of events in a functional and declarative fashion. It is especially very powerful in terms of error management and recovery. There are operators for retrying failed operations, handling timeouts and switching the processing to another value or pipeline in case of errors. There is however an inherent risk of "monad hell" when building a long chain of (sometimes nested) operators, and the code becomes harder to read. Splitting processing into multiple methods is a good strategy. Operators like zip, flatMap or concatMap are not necessarily meaningful if you are not much familiar with functional programming idioms. Also, not every processing is easy to express as pipelines, especially when conditional branching is involved.
Kotlin coroutines have the advantage of writing code that doesn’t look like asynchronous code. Simple error management cases can be expressed with familiar try / catch blocks. Although not mentioned here, Kotlin coroutines support channels and selectors in the style of the Go programming language, which allows message passing between coroutines. That being said, more sophisticated error management such as retries need to be expressed manually. Last but not least: it remains important to be aware of how coroutines and asynchronous programming work.
Again, there is no definite answer as all models have their pros and cons, but with your own experience and preferences you will likely recognize what is the model you should use for a given situation. In the remaining of this book we will use different models depending on the examples that we write, but this does not mean that you cannot rewrite them better with a model that you prefer.
[Meijer12] Erik Meijer. 2012. Your Mouse is a Database. Queue 10, 3, Pages 20 (March 2012), 14 pages. DOI: dx.doi.org/10.1145/2168796.2169076
[RX] ReactiveX website. reactivex.io/
[KotlinCoroutines] Coroutines guide, Kotlin website. kotlinlang.org/docs/reference/coroutines/coroutines-guide.html
[Conway63] Melvin E. Conway. 1963. Design of a separable transition-diagram compiler. Commun. ACM 6, 7 (July 1963), 396-408. DOI: dx.doi.org/10.1145/366663.366704
[Loom] Project Loom website. openjdk.java.net/projects/loom/
[KeepKtCoroutines] Kotlin Coroutines KEEP design proposal. github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md
[Liskov88] B. Liskov and L. Shrira. 1988. Promises: linguistic support for efficient asynchronous procedure calls in distributed systems. In Proceedings of the ACM SIGPLAN 1988 conference on Programming language design and implementation (PLDI'88), R. L. Wexelblat (Ed.). ACM, New York, NY, USA, 260-267.


I am a simple person as like you but every person have something different I'm also. There are lot of things I would like to do, to see, to hear, to touch, to smell, to speak and to experience. I like to play, study, read, write, think and so on.
Computer developers are engineers that typically work in either computer hardware or software. Computer software engineers develop a variety of software, including games, control systems, operating systems and applications. Computer hardware engineers produce the working physical components within the computer. They are responsible for analyzing and testing equipment to ensure the components work properly.


Display ONLY on Desktop?
-
mindcrash
I am wondering if it's possible to set Synergy to only display on my Desktop. That is, I don't want it to show in front of my active application.
Thanks!
-
Greg Hurrell
Yes, it is. See the release notes for version 1.3:
http://wincent.com/a/products/synergy-classic/history/
[Feature] New "power user" feature (accessible from command-line), display floater on desktop. To activate, enter "defaults write org.wincent.Synergy desktopFloater -bool true" in the Terminal and restart Synergy. To deactivate, enter "defaults write org.wincent.Synergy desktopFloater -bool false".
-
mindcrash
Great, thanks for the tip.
Awesome program by the way!
-
mindcrash
Ok, here's a related question, and I promise this is the last you'll hear from me.
Now that the app is set using the command posted above, is it possible to adjust a setting so that if I click the floater it will come to the foreground, like a regular application?
Thanks!
-
Greg Hurrell
Not at the moment.
The Floater has always been "transparent" to mouse clicks, as it's intended for temporary display only (fade in, fade out) and isn't meant to get in the way.
That was the original rationale. Having said that, there is a feature request in the database to make it clickable, and I have various ideas for things that can be added once that's implemented.
Relevant links:
"Click to dismiss Floater": http://wincent.com/a/support/bugs/show_bug.cgi?id=274
"Draggable Floater": http://wincent.com/a/support/bugs/show_bug.cgi?id=282
Another thing worth mentioning here is that if you have Growl installed you can configure it to display notifications from Synergy, and that will offer you some customization options as well. Note that normally you'd choose either the Synergy Floater or the Growl notifications, not both.
Reply
This topic is now closed.


Practice Summary - {{currentPractice.practiceName}}
{{currentPractice.numberOfQuestions}}{{currentPractice.getNumberOfIncorrectAnswers()}}{{currentPractice.getNumberOfCorrectAnswers()}}
Note: You can use the Start/Resume Test buttons to activate Test Mode with countdown timer!
Note: This package comes with detailed explanations. Please read this to understand the structure of the explanation.
Read Me


Genesis 44:14
And Judah and his brethren came to Joseph's house; for he was yet there: and they fell before him on the ground.


"I want to inspire people. I want them to say because of you I didn’t quit!"
I have had a passion for health and fitness for as long as I can remember! I am lucky enough to have been able to turn my favorite hobby into an interesting and rewarding career that means the world to me!
As a certified group fitness instructor and personal trainer my experience includes teaching fitness classes at several area gyms managing the Fitness Center at a local facility, conducting personal training sessions with clients ranging in age from pre-teen to 70+ years of age and acting as the Strength and Conditioning Coach for a nearby high school.
 I understand firsthand the challenges of blending your fitness plan and health goals with the demands of a job, a constantly changing schedule and a busy family! Schedule an appointment at Living Fitness and I will use both my personal and professional experience to help develop a fitness for you that will work and that will help you feel happier, healthier, more confident and more energetic! I promise you… fitness can be FUN!! It’s never too late to start!
"Never give up on a dream just because of the time that it will take to accomplish it. Time will pass away."
I have been married for 22 years and blessed to be the mother of two teenagers. I have a Bachelors degree in Business from Miami University and have been certified in group fitness and certified through AAAI/ISMA as a personal trainer. My fitness career began 20 years ago when I began taking fitness classes at a local gym. I quickly realized that I loved fitness and needed to make it a daily routine in my life. I became a certified group instructor through AFAA and focused on inspiring, challenging, and motivating others participating in my classes. I then expanded my love for health and fitness to include personal training so that I could work closely with clients and customize their fitness programs to include specific goals that improve their overall fitness and health.
My desire when training a client or instructing a group class is to help every client have a fun, positive, and motivating experience. I want to help them gain confidence in what they are trying to accomplish no matter what that goal is. Fitness is a lifestyle with no finish line, therefore we need to incorporate fitness into our daily lives in order to maintain better health, strength, balance and mental well being. You are never too late to start a fitness regimen or commit yourself to leading a healthier lifestyle.


| Australia | Canberra | National Library of Australia | 0 |
| Canada | Vancouver (BC) | BC Libraries Cooperative | 0 |
| Czech Republic | Prague | National Library of Technology | 0 |
| Germany | Berlin | Universitätsbibliothek Technische Universität Berlin | 2 |
| Germany | Göttingen | Gemeinsamer Bibliotheksverbund (GBV Göttingen) | 0 |
| Germany | Hannover | Technische Informationsbibliothek (TIB) | 20 |
| India | Kolkata | National Virtual Library of India | 0 |
| Norway | Bergen | University of Bergen Library | 1 |
| Norway | Oslo | University of Oslo Library | 6 |
| Switzerland | Basel | Information services for Swiss university libraries and Swiss National Library | 20 |
| US | Ann Arbor (MI) | Ann Arbor District Library | 1 |
| US | Evanston (IL) | Northwestern University Libraries | 0 |
| US | New York (NY) | New York Public Library | 0 |
| US | Princeton (NJ) | Princeton University Library | 2 |
| US | San Francisco / Oakland (CA) | California College of the Arts | 0 |
| US | Sleepy Hollow (NY) | Rockefeller Archive Center | 0 |


BeatRoutes
Log in with Spotify
Argentina
Australia
Austria
Belgium
Brazil
Bulgaria
Canada
Chile
China
Colombia
Costa Rica
Croatia
Cuba
Czech Republic
Denmark
Estonia
Fiji
Finland
France
Georgia
Germany
Greece
Hong Kong
Hungary
Iceland
India
Indonesia
Ireland
Israel
Italy
Japan
Kenya
Latvia
Lithuania
Luxembourg
Malaysia
Mexico
Montenegro
Netherlands
New Zealand
Norway
Paraguay
Peru
Philippines
Poland
Portugal
Romania
Russia
San Marino
Serbia
Slovakia
Slovenia
South Africa
South Korea
Spain
Sweden
Switzerland
Taiwan
The Vatican
Tobago
Trinidad
Turkey
UK
USA
Ukraine
Uruguay
Venezuela
Create Playlist!
x


Chinese has classifiers (measure words) for nouns.
English has this too. There are a "board" of directors, a "flock" of birds, a "school" of fish, and many more collective nouns. Some of them are rather humorous. If you show this list to a native English speaker, they will probably laugh. Most of these are not serious, and you can get away with saying "group" for everything. Likewise, in Chinese, you can just say "個" and people will understand.  My homework involved finding the collective nouns for various items based on a picture. Wiktionary has this information, so I decided to extract the data for all the nouns.
I'm keeping track of these in a file,
en-wiktionaryClassifiersDefinitions.txt.
Intriguingly, some of them are the same as English. 頭 means head (even on a human). 一頭牛 (yī tóu niú) is one "head" of cattle. The collective noun for many cattle in English is "drove", but the singular is "head". So it's similar. Likewise, 雙 means pair, and 一雙鞋子 (yī shuāng xiézi) is one "pair" of shoes.
In my dictionary, I combine the measure word with the noun, and consider it to be a single word object. Please feel free to add more of these with their nouns as you discover them.


mess - a javascript animation thing
choose your variant:
original - random sized circles that generate quickly
slow - random sized circles that generate slowly
ten - like the original, but limits it to ten circles, also random widths
color - colorful circles, also with random widths
akiraonion


SSHFS Admin
Downloads
For Windows users
- Open Explorer
- Enter %USERPROFILE% in the address bar
- Create folder sshfs if doesn't have one
- Save config.ini and ssh.key to folder sshfs
CAUTION
The ssh.key is only available once.
Regenerate one if you lose it.
Download the new key after regenerate.


{{}} Southbound - {{ selectedDate }} Service
{{ departureStation.stop_name }} |
to |
{{ arrivalStation.stop_name }} |
|---|---|---|
| Get Station Info | Get Station Info |
| Train No. | Date | Departure Time | Duration | Arrival Time | Route |
|---|---|---|---|---|---|
| {{trip.tripId}} | {{trip.timePeriod}} | {{trip.departureTime}} | {{trip.totalTripTime}} | {{trip.arrivalTime}} | map |


MKIAPViewController Class Reference
| Inherits from | UITableViewController |
| Conforms to | MKTableCellDelegate |
| Declared in | MKIAPViewController.h |
Overview
Overview
MKIAPViewController provides a store front interface for your inApp Purchases. This is drop in store front, just call the default initalizer and place the instance in your view controller stack.
Results of purchase and request are sent to the MKIAPObserver. The MKIAPObserver has one required method.
This class works along with MKIAPController and MKTableCellIAP.
MKIAPViewController Supports ARC
Tasks
Creating Instances
-
- initWithIdentifiers:observer:
Returns an intialized instance of the MKIAPViewController Class.
Products
-
items
An array of the availble inApp Purchase Items
Observer
Instance Methods
initWithIdentifiers:observer:
Returns an intialized instance of the MKIAPViewController Class.
- (id)initWithIdentifiers:(NSSet *)identifiers observer:(id<MKIAPObserver>)observer
Parameters
- identifiers
- A set of your inApp Purchase identifiers.
Required
- observer
- A designated observer. The observer must meet the MKIAPObserver protocol.
Required
Return Value
MKIAPViewController instance
Declared In
MKIAPViewController.h


I combine creative and viewpoints to create world-class Web and mobile applications.
iToros Mobile App 2.0, 2019
CSUDH's iToros transformation into a native mobile application, improving ease of use and engagement
Program Finder V2, 2019
Rework with sorting/filtering of the previous degree program finder using Isotope library
CSUDH Website Refresh, 2018
Campus wide redesign of CMS templates into responsive and intutive webpages
Campaign Landing Pages, 2017
Mobile-first approach to increase engagement for CSUDH's ad campaign landing pages
Shopping Engine Proof-of-Concept Prototype, 2017
A project for a client showcasing a shopping search engine similar to Google Shopping Search using Semantics3 API
Bloc Box, 2016
A UI/UX project to design a web app to share your notes, ideas, and files similar to Pinterest
Bloc Chat, 2016
A real-time chat client built with AngularJS and utilizes Firebase NoSQL DB to store information
Bloc Jams, 2016
A fully featured online music player built using jQuery and AngularJS


Classes
- AudioChannel
- AudioDetails
- Call
- CallCreationInfo
- CallEndEvent
- CallException
- Calls
- ConferenceEntryOptions
- RingbackToneURLs
- UserToUserInfo
- VideoChannel
- VideoDetails
Members
-
<static> AlertType :string
-
Enumeration describing possible call alert types for an incoming call.
Type:
- string
See:
Properties:
Name Type Default Description AUTO_CALLBACK
string AUTO_CALLBACK Auto callback.
AUTOMATIC_CALL_DISTRIBUTION_EXTERNAL
string AUTOMATIC_CALL_DISTRIBUTION_EXTERNAL Automatic call distribution external.
AUTOMATIC_CALL_DISTRIBUTION_INTERNAL
string AUTOMATIC_CALL_DISTRIBUTION_INTERNAL Automatic call distribution internal.
DIRECT_AGENT_CALL_EXTERNAL
string DIRECT_AGENT_CALL_EXTERNAL Direct agent call external.
DIRECT_AGENT_CALL_INTERNAL
string DIRECT_AGENT_CALL_INTERNAL Direct agent call internal.
DIRECT_AGENT_CALL_PRIORITY
string DIRECT_AGENT_CALL_PRIORITY Direct agent call priority.
EXTERNAL
string EXTERNAL External.
HOLD_RECALL
string HOLD_RECALL Hold recall.
INTERCOM
string INTERCOM Intercom.
INTERNAL
string INTERNAL Internal.
NONE
string NONE None.
PRIORITY
string PRIORITY Priority.
TRANSFER_RECALL
string TRANSFER_RECALL Transfer recall.
UNSPECIFIED
string UNSPECIFIED Unspecified.
-
<static> AudioMode :string
-
Enumeration describing possible audio modes for the call.
Type:
- string
Properties:
Name Type Default Description SEND_RECEIVE
string SEND_RECEIVE Bi-directional audio. Same as setting audio mode to ENABLE.
DISABLE
string DISABLE Disabled audio
ENABLE
string ENABLE Enabled audio
When enabled, audio is set to be send-receive, that is, bidirectional.
-
<static> CallDenialReason :string
-
Reasons that can be used for denying an incoming call.
Type:
- string
Properties:
Name Type Default Description BUSY
string BUSY Busy.
FORBIDDEN
string FORBIDDEN Forbidden.
TEMPORARILY_UNAVAILABLE
string TEMPORARILY_UNAVAILABLE Temporary Unavailable.
-
<static> CallEndReason :string
-
Reasons that can be used for ending a call.
Type:
- string
Properties:
Name Type Default Description CALL_DISCONNECTED
string CALL_DISCONNECTED Call ended remotely.
CALL_DISCONNECTED_UNKNOWN_REASON
string CALL_DISCONNECTED_UNKNOWN_REASON Call disconnected with an unknown reason.
CALL_ENDED_LOCALLY
string CALL_ENDED_LOCALLY Call ended locally.
DISCONNECTED_BY_CONFERENCE_MODERATOR
string DISCONNECTED_BY_CONFERENCE_MODERATOR Call disconnected by the conference moderator or administrator
CONFERENCE_TERMINATED
string CONFERENCE_TERMINATED The conference was terminated
DTLS_FAILURE
string DTLS_FAILURE Call failed due to a DTLS error in the media transport reported by the far end
ICE_FAILURE
string ICE_FAILURE ICE error reported by the far end
-
<static> CallError :string
-
An enumeration of call errors.
Type:
- string
Properties:
Name Type Default Description AUTHENTICATION_ERROR
string AUTHENTICATION_ERROR Reported when local user's authentication with the network has failed. This can happen when user-supplied credentials do not match the credentials the server expects to receive from the end user.
AUTO_HOLD_FAILED
string AUTO_HOLD_FAILED Reported when a request by the client to start, accept or unhold a call results in an automatic hold of another currently active call and that hold operation fails.
BUSY
string BUSY Reported when the request is rejected by the server due to resource being busy.
For example, a point-to-point call may fail if the called party returns a busy indication, or if there are no local line appearances available on the server side to initiate the call.CALL_STATE_MISMATCH
string CALL_STATE_MISMATCH Reported when the request cannot be supported at the moment due to the current state of the call.
For example, a hold request while the call is being transferred, is not allowed.CALL_NOT_FOUND
string CALL_NOT_FOUND Call is not found.
CAPABILITY_NOT_ALLOWED
string CAPABILITY_NOT_ALLOWED Operation is not allowed currently.
CODEC_MISMATCH
string CODEC_MISMATCH Reported when a VoIP session cannot be established due to incompatibility of codecs between the local and remote parties.
DEVICES_NOT_FOUND
string DEVICES_NOT_FOUND Media devices are not found.
MICROPHONE_NOT_FOUND
string MICROPHONE_NOT_FOUND Microphone is not found.
SPEAKERS_NOT_FOUND
string SPEAKERS_NOT_FOUND Speakers are not found.
CAMERA_NOT_FOUND
string CAMERA_NOT_FOUND Camera is not found.
FAILED
string FAILED Operation has failed. The failure reason was not specified.
TCP_TLS_TURN_NOT_SUPPORTED
string TCP_TLS_TURN_NOT_SUPPORTED Edge Turn TLS/TCP error.
ENDED
string ENDED Call is ended.
IN_PROGRESS
string IN_PROGRESS Operation has failed because another call operation is in progress.
INTERNAL_ERROR
string INTERNAL_ERROR Operation has failed due to an internal error.
For example, if the network connection is lost after the API call is made by the application but before the API call can be processed, this error code is returned.INVALID_PARAMETER
string INVALID_PARAMETER Reported when an invalid parameter is used for the request.
For example, if the application tries to add a participant but provides an empty participant address, this error is reported.LINE_RESERVATION_ERROR
string LINE_RESERVATION_ERROR Reported when a line reservation attempt is failed by the server. It should be noted that the line appearance id and the line appearance owner are required to reserve a line appearance when the call is initiated on a bridged line appearance.
If, for some reason, the line reservation request fails, this error code is reported.
This error code is only applicable to communication system environments where bridging and bridged line appearances are supported.MEDIA_CREATION_FAILURE
string MEDIA_CREATION_FAILURE Reported when local audio and/or video resources cannot be initialized successfully.
MEDIA_START_FAILURE
string MEDIA_START_FAILURE Reported when local audio and/or video resources cannot be started successfully.
MULTIPLE_CALLS_NOT_SUPPORTED
string MULTIPLE_CALLS_NOT_SUPPORTED Reported when the call server doesn't support multiple simultaneous calls.
MULTIPLE_VIDEO_CHANNELS_NOT_SUPPORTED
string MULTIPLE_VIDEO_CHANNELS_NOT_SUPPORTED Multiple video channels are not supported.
MUTE_STATE_MISMATCH
string MUTE_STATE_MISMATCH Mute state mismatch.
NO_AVAILABLE_CALL_APPEARANCE
string NO_AVAILABLE_CALL_APPEARANCE Reported when the local user call initiation attempt fails because the Avaya Communication Manager has no idle line appearance available for the call.
NOT_AUTHORIZED
string NOT_AUTHORIZED Reported when add or remove operations of the participant cannot be supported because the local user does not have moderator privileges to perform these actions.
NOT_FOUND
string NOT_FOUND Reported when the called party address is not known to the server.
For example, if the user dials an invalid telephone number, this error code is reported.NOT_LICENSED
string NOT_LICENSED Reported when an operation is denied because the user does not have the license to perform the operation.
For example, a video add request may be denied using this error code when the user account is not licensed to support video calls.NOT_REGISTERED
string NOT_REGISTERED Reported when an operation fails because the server assumes that the user is not registered.
REDIRECTED
string REDIRECTED Operation was redirected to another destination.
REMOTE_CALL_NOT_ALERTING
string REMOTE_CALL_NOT_ALERTING Reported when an attempt to answer an incoming call locally at a remote extension (e.g., at a bridged line appearance extension) has failed because the call was already answered remotely.
REMOTE_MEDIA_PROCESSING_FAILURE
string REMOTE_MEDIA_PROCESSING_FAILURE Reported when the application tries to accept an incoming call, but this attempt fails because the remote audio and/or video information could not be applied to local audio/video resources.
SEND_ERROR
string SEND_ERROR Reported when a network message could not be sent due to a network connection error.
SERVER_ERROR
string SERVER_ERROR Reported when the request has failed due to an internal server error.
SESSION_ID_NOT_FOUND
string SESSION_ID_NOT_FOUND Reported when an invalid or expired sessionId is used for an MPaaS call.
SESSION_ID_ALREADY_CONNECTED
string SESSION_ID_ALREADY_CONNECTED Reported when the same sessionId is used for two MPaaS calls at the same time.
SUCCESS
string SUCCESS Operation completed successfully. This return code is not used.
TIMEOUT
string TIMEOUT Reported when local time-out occurs while sending a network message or a response to the message was not received from the server in time.
TRANSPORT_ERROR
string TRANSPORT_ERROR Reported when the request could not be sent to the network server.
UNDEFINED
string UNDEFINED Operation has failed because an unknown error occurred. This error code is not used.
UNHANDLED_VIDEO_DIRECTION
string UNHANDLED_VIDEO_DIRECTION Unhandled video direction.
USER_NOT_FOUND
string USER_NOT_FOUND Reported when the called party address is not known to the server.
For example, if the user dials an invalid telephone number, this error code is reported.USER_TEMPORARILY_UNAVAILABLE
string USER_TEMPORARILY_UNAVAILABLE Reported when the called party has no device registered on his/her behalf, thus, making call setup not possible at this point in time.
VIDEO_DENIED
string VIDEO_DENIED Reported when a mid-call video add request was denied by the remote party.
VIDEO_DEESCALATION_NOT_SUPPORTED
string VIDEO_DEESCALATION_NOT_SUPPORTED Reported when browser does not support video deescalation from a remote endpoint/gateway.
WEBRTC_NOT_SUPPORTED
string WEBRTC_NOT_SUPPORTED WEBRTC is not supported by the browser.
STATISTICS_UNAVAILABLE
string STATISTICS_UNAVAILABLE Statistics information is unavailable.
MEDIA_PRESERVED
string MEDIA_PRESERVED Signaling path for the call has failed but media is preserved.
SERVICE_TEMPORARILY_UNAVAILABLE
string SERVICE_TEMPORARILY_UNAVAILABLE Reported when service is temporarily unavailable.
SERVICE_UNAVAILABLE
string SERVICE_UNAVAILABLE Reported when service is unavailable.
VIDEO_ONLY_CALL_NOT_SUPPORTED
string VIDEO_ONLY_CALL_NOT_SUPPORTED Video only call is not supported.
VIDEO_REJECTION_NOT_SUPPORTED
string VIDEO_REJECTION_NOT_SUPPORTED Video was rejected by far-end and the browser does not support reverting to audio only.
COLLABORATION_ONLY_CALL_NOT_SUPPORTED
string COLLABORATION_ONLY_CALL_NOT_SUPPORTED Collaboration only call is not supported.
-
<static> CallProtocolType :string
-
Enumeration describing possible call protocol types.
Type:
- string
Properties:
Name Type Default Description APPLICATION
string APPLICATION Application protocol type.
HTTP
string HTTP HTTP protocol type.
-
<static> CallStates :string
-
Enumeration describing possible call states.
Type:
- string
Properties:
Name Type Default Description FAR_END_RENEGOTIATING
string far_end_renegotiating Call is being renegotiated by the far-end of the call.
IDLE
string idle The initial state of the call prior to it was started.
IGNORED
string ignored Indicates that the call previously ALERTING, was ignored by the local user.
INITIATING
string initiating Indicates that the call is initiating after it was started but before the remote party is alerting (ringing).
REMOTE_ALERTING
string remote_alerting Indicates that the remote party is currently alerting (ringing).
RENEGOTIATING
string renegotiating The call is renegotiating by the local user to perform an operation such as adding video.
FAILED
string failed The call failed to start or was ended abnormally.
ALERTING
string alerting The call is alerting locally.
HELD
string held The call was held by the local user.
ENDED
string ended The call ended.
ENDING
string ending The call is in the process of ending.
HOLDING
string holding The call is in the process of being put on hold by the local user.
UNHOLDING
string unholding The call is in the process of being unheld by the local user.
ESTABLISHED
string established The call is currently active.
BEING_TRANSFERRED
string being_transferred The remote user is transferring this call to another party.
TRANSFERRING
string transferring The local user is transferring this call to another party.
-
<static> CallType :string
-
Type:
- string
Properties:
Name Type Default Description CallTypeDefaultRegisteredService
string DefaultRegisteredService Call dials through the UC infrastructure.
CallTypeHttpMeetMe
string HttpMeetMe Join conferencing meetme meeting without going through the UC infrastructure.
-
<static> DTMFTone :string
-
Enumeration describing DTMF tones.
Type:
- string
Properties:
Name Type Default Description A
string A B
string B C
string C D
string D STAR
string * POUND
string # ONE
string 1 TWO
string 2 THREE
string 3 FOUR
string 4 FIVE
string 5 SIX
string 6 SEVEN
string 7 EIGHT
string 8 NINE
string 9 ZERO
string 0
-
<static, readonly> MediaDirection :string
-
Enumeration describing DTMF tones.
Type:
- string
Properties:
Name Type Default Description RECV_ONLY
string recvonly Media is received but not sent.
SEND_ONLY
string sendonly Media is sent but no media is received.
SEND_RECV
string sendrecv Media is sent and received.
INACTIVE
string inactive No media flow. No media is transmitted or received.
DISABLE
string disable Media direction has been disabled.
UNDEFINED
string undefined Media direction has not been set.
-
<static> TransferProgressCode :string
-
Enumeration describing possible transfer operation progress states.
Type:
- string
Properties:
Name Type Default Description ACCEPTED
string ACCEPTED Transfer accepted.
NONE
string NONE None.
REMOTE_CALL_ESTABLISHED
string REMOTE_CALL_ESTABLISHED Remote call is established.
REMOTE_CALL_IN_PROGRESS
string REMOTE_CALL_IN_PROGRESS Remote call is in progress.
REMOTE_CALL_STARTED
string REMOTE_CALL_STARTED Remote call is started.
STARTED
string STARTED Transfer is started.
-
<static> VideoChannelDisabledReason :string
-
Enumeration describing possible reasons for disabling a video channel.
Type:
- string
Properties:
Name Type Default Description NONE
string none Associated video channel object is enabled.
DISABLED_BY_LOCAL_USER
string disabledByLocalUser The local user removed video from the call.
DISABLED_BY_REMOTE_USER
string disabledByRemoteUser The remote user removed video from the call.
DISABLED_DUE_TO_CPU_OVERLOAD
string disabledDueToCpuOverload High CPU utilization is detected.
DISABLED_DUE_TO_INSUFFICIENT_BANDWIDTH
string disabledDueToInsufficientBandwidth There is insufficient bandwidth to support video.
DISABLED_DUE_TO_LOCAL_PLATFORM_LIMITATIONS
string disabledDueToLocalPlatformLimitations Based on local configuration information and/or the capabilities of the local platform, the video channel cannot be supported.
DISABLED_DUE_TO_LOCAL_LAYOUT_SELECTION
string disabledDueToLocalLayoutSelection Based on local video layout selection, the video channel cannot be supported.
For example, if the user selects a video layout of 4 video streams but the incoming multi stream switched (MSS) video offer has 9 video streams, 5 of the video streams (streams 5, 6, 7, 8, 9) are disabled.DISABLED_DUE_TO_MISSING_H264_PLUGIN
string disabledDueToMissingH264Plugin Missing H.264 plugin
-
<static> VideoMode :string
-
Enumeration describing possible video modes for the call.
Type:
- string
Properties:
Name Type Default Description SEND_RECEIVE
string SEND_RECEIVE Bi-directional video.
RECEIVE_ONLY
string RECEIVE_ONLY Receive-only video.
SEND_ONLY
string SEND_ONLY Send-only video.
INACTIVE
string INACTIVE Inactive. Returns if video for the call is inactive.
DISABLE
string DISABLE Disabled video (i.e. video was removed).
-
<static, readonly> VideoNetworkSignalingType :string
-
Enumeration describing video network signaling types.
Type:
- string
Properties:
Name Type Default Description INDEFINITE
string indefinite Indefinite.

SUPPORTED
string supported Supported.