Quantcast
Channel: Sleepless Dev
Viewing all articles
Browse latest Browse all 217

Invokable Promise making reactive Java programming more fluid

$
0
0
First cut of invokable promises, 

Please provide feedback. 



Reakt, Java reactive programming lib, supports invokable promises so service methods could return a Promise.

Invokable Promise

        employeeService.lookupEmployee("123")
.then((employee)-> {...}).catchError(...).invoke();
NOTE: QBit Microservices Lib version 1.4 (coming soon) will support generation of client stubs (local and remote) that return Reakt Promises. Having the client proxy method return the promise instead of taking a callback. With QBit you will be able to use Callback's on the service side and Promises on the client proxy side. Callbacks are natural for the service, and Promises are natural for the client.
But you do not have to use QBit to use Reakt's invokable promises.
Let's walk through an example of using a Reakt invokable promises.
Let's say we are developing a service discovery service with this interface.

ServiceDiscovery interface (Example service that does lookup)

interfaceServiceDiscovery {
Promise<URI>lookupService(URIuri);

default voidshutdown() {}
default voidstart() {}
}
Note that ServiceDiscovery.lookupService is just an example likeEmployeeService.lookupEmployee was just an example.
Notice the lookupService returns a Promise (Promise<URI> lookupService(URI uri)). To call this, we can use an invokable promise. The service side will return an invokable promise, and the client of the service will use that Promise to register its thenthenExpectthenMap, and/orcatchError handlers.
Here is the service side of the invokable promise example.

Service Side of invokable promise

classServiceDiscoveryImplimplementsServiceDiscovery {

@Override
publicPromise<URI>lookupService(URIuri) {
return invokablePromise(promise -> {
if (uri ==null) {
promise.reject("URI was null");
} else {
...
// DO SOME ASYNC OPERATION WHEN IT RETURNS CALL RESOLVE.
promise.resolve(successResult);
}
});
}
}
Notice to create the invokablePromise we use Promises.invokablePromise which takes aPromise Consumer (static <T> Promise<T> invokablePromise(Consumer<Promise<T>> promiseConsumer)).
The lookupService example returns a Promise and it does not execute its body until theinvoke (Promise.invoke) on the client side of the equation is called.
Let's look at the client side.

Client side of using lookupService.

        serviceDiscovery.lookupService(empURI)
.then(this::handleSuccess)
.catchError(this::handleError)
.invoke();
The syntax this::handleSuccess is a method reference which can be used as Java 8 lambda expressions. We use method references to make the example shorter.
Here is a complete example with two versions of our example service.

Complete example from unit tests

packageio.advantageous.reakt.promise;

importorg.junit.After;
importorg.junit.Before;
importorg.junit.Test;

importjava.net.URI;
importjava.util.Queue;
importjava.util.concurrent.*;
importjava.util.concurrent.atomic.AtomicBoolean;
importjava.util.concurrent.atomic.AtomicReference;

import staticio.advantageous.reakt.promise.Promises.*;
import staticorg.junit.Assert.*;

publicclassInvokablePromise {


finalURI successResult =URI.create("http://localhost:8080/employeeService/");
ServiceDiscovery serviceDiscovery;
ServiceDiscovery asyncServiceDiscovery;
URI empURI;
CountDownLatch latch;
AtomicReference<URI> returnValue;
AtomicReference<Throwable> errorRef;

@Before
publicvoidbefore() {
latch =newCountDownLatch(1);
returnValue =newAtomicReference<>();
errorRef =newAtomicReference<>();
serviceDiscovery =newServiceDiscoveryImpl();
asyncServiceDiscovery =newServiceDiscoveryAsyncImpl();
asyncServiceDiscovery.start();
empURI =URI.create("marathon://default/employeeService?env=staging");
}

@After
publicvoidafter() {
asyncServiceDiscovery.shutdown();
}

publicvoidawait() {
try {
latch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
thrownewIllegalStateException(e);
}
}

@Test
publicvoidtestServiceWithReturnPromiseSuccess() {
serviceDiscovery.lookupService(empURI).then(this::handleSuccess)
.catchError(this::handleError).invoke();
await();
assertNotNull("We have a return", returnValue.get());
assertNull("There were no errors", errorRef.get());
assertEquals("The result is the expected result", successResult, returnValue.get());
}


@Test
publicvoidtestServiceWithReturnPromiseFail() {


serviceDiscovery.lookupService(null).then(this::handleSuccess)
.catchError(this::handleError).invoke();

await();
assertNull("We do not have a return", returnValue.get());
assertNotNull("There were errors", errorRef.get());
}


@Test
publicvoidtestAsyncServiceWithReturnPromiseSuccess() {
asyncServiceDiscovery.lookupService(empURI).then(this::handleSuccess)
.catchError(this::handleError).invoke();
await();
assertNotNull("We have a return from async", returnValue.get());
assertNull("There were no errors form async", errorRef.get());
assertEquals("The result is the expected result form async", successResult, returnValue.get());
}


@Test
publicvoidtestAsyncServiceWithReturnPromiseFail() {


asyncServiceDiscovery.lookupService(null).then(this::handleSuccess)
.catchError(this::handleError).invoke();

await();
assertNull("We do not have a return from async", returnValue.get());
assertNotNull("There were errors from async", errorRef.get());
}

@Test (expected =IllegalStateException.class)
publicvoidtestServiceWithReturnPromiseSuccessInvokeTwice() {
finalPromise<URI> promise = serviceDiscovery.lookupService(empURI).then(this::handleSuccess)
.catchError(this::handleError);
promise.invoke();
promise.invoke();
}

@Test
publicvoidtestIsInvokable() {
finalPromise<URI> promise = serviceDiscovery.lookupService(empURI).then(this::handleSuccess)
.catchError(this::handleError);

assertTrue("Is this an invokable promise", promise.isInvokable());
}


privatevoidhandleError(Throwableerror) {
errorRef.set(error);
latch.countDown();
}

privatevoidhandleSuccess(URIuri) {
returnValue.set(uri);
latch.countDown();
}


interfaceServiceDiscovery {
Promise<URI>lookupService(URIuri);

default voidshutdown() {
}

default voidstart() {
}
}

classServiceDiscoveryImplimplementsServiceDiscovery {

@Override
publicPromise<URI>lookupService(URIuri) {
return invokablePromise(promise -> {

if (uri ==null) {
promise.reject("URI was null");
} else {
promise.resolve(successResult);
}
});
}
}


classServiceDiscoveryAsyncImplimplementsServiceDiscovery {

finalExecutorService executorService;

finalQueue<Runnable> runnables;

finalAtomicBoolean stop;

publicServiceDiscoveryAsyncImpl() {
executorService =Executors.newSingleThreadExecutor();
runnables =newLinkedTransferQueue<>();
stop =newAtomicBoolean();
}

@Override
publicPromise<URI>lookupService(URIuri) {
return invokablePromise(promise -> {
runnables.offer(() -> {
if (uri ==null) {
promise.reject("URI was null");
} else {
promise.resolve(URI.create("http://localhost:8080/employeeService/"));
}
});
});
}

@Override
publicvoidshutdown() {
stop.set(true);
executorService.shutdown();
}

@Override
publicvoidstart() {
executorService.submit((Runnable) () -> {

try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
if (stop.get())break;
Runnable runnable = runnables.poll();
while (runnable !=null) {
runnable.run();
runnable = runnables.poll();
}
}

});
}
}
}

Viewing all articles
Browse latest Browse all 217

Latest Images

Trending Articles



Latest Images