| Title: |
| Notice: Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| . |
| http://www.apache.org/licenses/LICENSE-2.0 |
| . |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| Title: Asynchronous Services |
| |
| #Introduction |
| The R6 OSGi specifications include support for Asynchronous programming using OSGi services. |
| Apache Aries aims to provide small, compliant implementations of these specifications to enable asynchronous |
| programming in enterprise applications. The two key specifications are OSGi Promises and the Async service. |
| |
| #OSGi Promises |
| |
| One of the fundamental pieces of an asynchronous programming system is the *Promise*. A Promise is a holder type that |
| represents an asynchronous calculation or computation. Since Java 5 the JDK has contained *java.util.concurrent.Future* to perform this |
| function. Java's Future type is, however, fatally flawed as it has no callback to notify the user when it resolves. Instead the |
| user must make a blocking call to *get()*. |
| |
| OSGi promises fix this problem by defining a Promise interface which allows the user to register callbacks which will be |
| called when the promise *resolves*. These callbacks are lambda friendly SAM types, but the Promise API itself has no |
| dependencies on Java 8. The Aries version (org.apache.aries.async.promise.api) has a minimum requirement of JDK 6. |
| |
| ##Creating OSGi Promises |
| |
| Creating a Promise is easy. The *org.osgi.util.promise.Deferred* type is a factory for a single promise, and can also be used to |
| resolve or fail the promise: |
| |
| Deferred<Integer> deferred = new Deferred<>(); |
| new Thread(() -> { |
| int result = calculateReallyHardSum(); |
| deferred.resolve(result); |
| }).start(); |
| |
| Promise<Integer> p = deferred.getPromise(); |
| ... |
| |
| But wait - what if something goes wrong? How do we signal to the user that there was a problem and the result is never coming? |
| The answer is very easy - Promises have the concept of *failure*. A promise will *either* resolve *or* fail at most once. For well |
| written code this rule is usually the same as "A promise will either resolve or fail *exactly* once". Promises are thread safe and |
| effectively immutable, meaning they can be shared with other code. |
| |
| Deferred<Integer> deferred = new Deferred<>(); |
| new Thread(() -> { |
| try { |
| int result = calculateErrorProneSum(); |
| deferred.resolve(result); |
| } catch (Exception e) { |
| deferred.fail(e); |
| } |
| }).start(); |
| |
| Promise<Integer> p = deferred.getPromise(); |
| ... |
| |
| ##Using OSGi Promises |
| |
| Once you have a promise, what do you do with it? It's easy to get the value from a promise using the *getValue()* method, |
| or you can use the *getFailure()* method to get the failure cause. Unfortunately both of these methods block until the promise |
| resolves, and whilst the *isDone()* method does tell you if the Promise has completed they really aren't the right way to use a |
| Promise. |
| |
| Promises work best when you register callbacks and/or transformations. The Promise API has a variety of useful methods for |
| doing work with the Promise when it resolves. For example we can run a task after the promise completes successfully: |
| |
| Promise<Integer> promise = ... |
| promise.then(p -> { |
| System.out.println("The calculator returned " + p.getValue()); |
| return null; |
| }); |
| |
| We can also register callbacks to handle failures: |
| |
| Promise<Integer> promise = ... |
| promise.then(p -> { |
| System.out.println("The calculator returned " + p.getValue()); |
| return null; |
| }, p -> p.getFailure().printStackTrace()); |
| |
| ### Chaining OSGi Promises |
| |
| In the previous examples our success callback returned *null* - why? Well the return value from a success callback is always |
| a promise (null is a shortcut for a promise resolved with null). The promise returned by the callback represents an asynchronous |
| execution flow in a process known as "chaining". The overall completion of this chain is represented by a third promise, returned |
| to the caller of the *then()* method. |
| |
| 1. The caller registers a success callback, and receives a "chained" promise |
| 2. The original promise completes successfully |
| 3. The success callback runs and returns a promise representing another piece of asynchronous work |
| 4. The promise returned by the success callback completes successfully |
| 5. The "chained" promise completes with the same value as the promise from step 4. |
| |
| ##Other Promise behaviours |
| |
| As well as simple callbacks Promises also provide advanced mapping and recovery features. For example a promise can be |
| wrapped so that if the original work fails then a new value can be supplied using a recovery function. |
| |
| The *org.osgi.util.promise.Promises* utility class also provides useful functions for working with promises. For example helper |
| methods to wrap an existing value or failure in a Promise, or a way of aggregating a group of promises into a single promise. |
| |
| |
| #The Async service |
| |
| Most OSGi services have a synchronous API. This is usually the easiest way to think about, write, and use services. The main |
| problem with this is that long running service calls can cause applications to run slowly, and making the calls asynchronous is |
| both verbose and error-prone. |
| |
| The Async service is designed to take away the boilerplate code needed to invoke a service asynchronously, and to convert any |
| synchronous API into an asynchronous API, returning promises instead of values. |
| |
| ##Using the Async service |
| |
| The Async service is available in the service registry and is very easy to use - first we need to mediate the service. |
| Mediating is a bit like creating a mock object, the mediator records method calls made against it so that they can be |
| transformed into asynchronous calls. Mediating can apply to a concrete object, or to a ServiceReference. It is better to |
| use a ServiceReference when one is available, as the Async service can track the availability of the backing service. |
| |
| |
| Async async = ...; |
| ServiceReference<MyService> ref = ...; |
| MyService mediator = async.mediate(ref, MyService.class); |
| |
| *or* |
| |
| Async async = ...; |
| MyService svc = ...; |
| MyService mediator = async.mediate(svc, MyService.class); |
| |
| Once a service has been mediated the mediator should be called just like the real service object, and the return value passed to |
| the Async service's *call()* method. This returns a promise representing the asynchronous work. |
| |
| Promise<Integer> promise = async.call(mediator.calculateReallyHardSum()); |
| |
| ### Void methods |
| |
| Void methods don't have a return value to pass to the async service, and should use the no-args version of call instead. |
| |
| mediator.longRunningVoidMethod() |
| Promise<?> promise = async.call(); |
| |
| ### Fire and Forget calls |
| |
| Sometimes the user does not care when a piece of work finishes, or what value it returns, or even whether it was successful. |
| These sorts of calls are called "fire and forget" calls, and are also supported by the async service using the *execute()* method. |
| |
| The execute method still returns a promise, however this promise represents whether the fire and forget call successfully started |
| or not, not whether it has completed. |
| |
| # Getting Started |
| Releases of the Async implementation can be found in Maven Central [in the org.apache.aries.async group][1]. [This bundle][2] provides a convenient all-in-one download. |
| |
| The Asynchronous Services source code can be found in the Apache Aries codebase in the `async` directory: [https://svn.apache.org/repos/asf/aries/trunk/async][3] |
| |
| |
| [1]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.aries.async%22 "org.apache.aries.async" |
| [2]: http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22org.apache.aries.async%22 |
| [3]: https://svn.apache.org/repos/asf/aries/trunk/async |