/** * Copyright (C) 2009-2012 Typesafe Inc. */ package docs.future; //#imports1 import akka.dispatch.*; import akka.util.Timeout; //#imports1 //#imports2 import akka.util.Duration; import akka.japi.Function; import java.util.concurrent.Callable; import static akka.dispatch.Futures.future; import static java.util.concurrent.TimeUnit.SECONDS; //#imports2 //#imports3 import static akka.dispatch.Futures.sequence; //#imports3 //#imports4 import static akka.dispatch.Futures.traverse; //#imports4 //#imports5 import akka.japi.Function2; import static akka.dispatch.Futures.fold; //#imports5 //#imports6 import static akka.dispatch.Futures.reduce; //#imports6 //#imports7 import akka.dispatch.ExecutionContexts; import akka.dispatch.ExecutionContextExecutorService; //#imports7 import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.After; import org.junit.Before; import org.junit.Test; import akka.testkit.AkkaSpec; import akka.actor.Status.Failure; import akka.actor.ActorSystem; import akka.actor.UntypedActor; import akka.actor.ActorRef; import akka.actor.Props; import akka.pattern.Patterns; import static org.junit.Assert.*; public class FutureDocTestBase { ActorSystem system; @Before public void setUp() { system = ActorSystem.create("MySystem", AkkaSpec.testConf()); } @After public void tearDown() { system.shutdown(); } @Test public void useCustomExecutionContext() throws Exception { ExecutorService yourExecutorServiceGoesHere = Executors.newSingleThreadExecutor(); //#diy-execution-context ExecutionContextExecutorService ec = ExecutionContexts.fromExecutorService(yourExecutorServiceGoesHere); //Use ec with your Futures Future f1 = Futures.successful("foo", ec); // Then you shut the ec down somewhere at the end of your program/application. ec.shutdown(); //#diy-execution-context } @Test public void useBlockingFromActor() throws Exception { ActorRef actor = system.actorOf(new Props(MyActor.class)); String msg = "hello"; //#ask-blocking Timeout timeout = new Timeout(Duration.parse("5 seconds")); Future future = Patterns.ask(actor, msg, timeout); String result = (String) Await.result(future, timeout.duration()); //#ask-blocking assertEquals("HELLO", result); } @Test public void useFutureEval() throws Exception { //#future-eval Future f = future(new Callable() { public String call() { return "Hello" + "World"; } }, system.dispatcher()); String result = (String) Await.result(f, Duration.create(1, SECONDS)); //#future-eval assertEquals("HelloWorld", result); } @Test public void useMap() throws Exception { //#map Future f1 = future(new Callable() { public String call() { return "Hello" + "World"; } }, system.dispatcher()); Future f2 = f1.map(new Mapper() { public Integer apply(String s) { return s.length(); } }); int result = Await.result(f2, Duration.create(1, SECONDS)); assertEquals(10, result); //#map } @Test public void useMap2() throws Exception { //#map2 Future f1 = future(new Callable() { public String call() throws Exception { Thread.sleep(100); return "Hello" + "World"; } }, system.dispatcher()); Future f2 = f1.map(new Mapper() { public Integer apply(String s) { return s.length(); } }); //#map2 int result = Await.result(f2, Duration.create(1, SECONDS)); assertEquals(10, result); } @Test public void useMap3() throws Exception { //#map3 Future f1 = future(new Callable() { public String call() { return "Hello" + "World"; } }, system.dispatcher()); Thread.sleep(100); Future f2 = f1.map(new Mapper() { public Integer apply(String s) { return s.length(); } }); //#map3 int result = Await.result(f2, Duration.create(1, SECONDS)); assertEquals(10, result); } @Test public void useFlatMap() throws Exception { //#flat-map Future f1 = future(new Callable() { public String call() { return "Hello" + "World"; } }, system.dispatcher()); Future f2 = f1.flatMap(new Mapper>() { public Future apply(final String s) { return future(new Callable() { public Integer call() { return s.length(); } }, system.dispatcher()); } }); //#flat-map int result = Await.result(f2, Duration.create(1, SECONDS)); assertEquals(10, result); } @Test public void useSequence() throws Exception { List> source = new ArrayList>(); source.add(Futures.successful(1, system.dispatcher())); source.add(Futures.successful(2, system.dispatcher())); //#sequence //Some source generating a sequence of Future:s Iterable> listOfFutureInts = source; // now we have a Future[Iterable[Integer]] Future> futureListOfInts = sequence(listOfFutureInts, system.dispatcher()); // Find the sum of the odd numbers Future futureSum = futureListOfInts.map(new Mapper, Long>() { public Long apply(Iterable ints) { long sum = 0; for (Integer i : ints) sum += i; return sum; } }); long result = Await.result(futureSum, Duration.create(1, SECONDS)); //#sequence assertEquals(3L, result); } @Test public void useTraverse() throws Exception { //#traverse //Just a sequence of Strings Iterable listStrings = Arrays.asList("a", "b", "c"); Future> futureResult = traverse(listStrings, new Function>() { public Future apply(final String r) { return future(new Callable() { public String call() { return r.toUpperCase(); } }, system.dispatcher()); } }, system.dispatcher()); //Returns the sequence of strings as upper case Iterable result = Await.result(futureResult, Duration.create(1, SECONDS)); assertEquals(Arrays.asList("A", "B", "C"), result); //#traverse } @Test public void useFold() throws Exception { List> source = new ArrayList>(); source.add(Futures.successful("a", system.dispatcher())); source.add(Futures.successful("b", system.dispatcher())); //#fold //A sequence of Futures, in this case Strings Iterable> futures = source; //Start value is the empty string Future resultFuture = fold("", futures, new Function2() { public String apply(String r, String t) { return r + t; //Just concatenate } }, system.dispatcher()); String result = Await.result(resultFuture, Duration.create(1, SECONDS)); //#fold assertEquals("ab", result); } @Test public void useReduce() throws Exception { List> source = new ArrayList>(); source.add(Futures.successful("a", system.dispatcher())); source.add(Futures.successful("b", system.dispatcher())); //#reduce //A sequence of Futures, in this case Strings Iterable> futures = source; Future resultFuture = reduce(futures, new Function2() { public Object apply(Object r, String t) { return r + t; //Just concatenate } }, system.dispatcher()); Object result = Await.result(resultFuture, Duration.create(1, SECONDS)); //#reduce assertEquals("ab", result); } @Test public void useSuccessfulAndFailed() throws Exception { //#successful Future future = Futures.successful("Yay!", system.dispatcher()); //#successful //#failed Future otherFuture = Futures.failed(new IllegalArgumentException("Bang!"), system.dispatcher()); //#failed Object result = Await.result(future, Duration.create(1, SECONDS)); assertEquals("Yay!", result); Throwable result2 = Await.result(otherFuture.failed(), Duration.create(1, SECONDS)); assertEquals("Bang!", result2.getMessage()); } @Test public void useFilter() throws Exception { //#filter Future future1 = Futures.successful(4, system.dispatcher()); Future successfulFilter = future1.filter(Filter.filterOf(new Function() { public Boolean apply(Integer i) { return i % 2 == 0; } })); Future failedFilter = future1.filter(Filter.filterOf(new Function() { public Boolean apply(Integer i) { return i % 2 != 0; } })); //When filter fails, the returned Future will be failed with a scala.MatchError //#filter } public void sendToTheInternetz(String s) { } public void sendToIssueTracker(Throwable t) { } @Test public void useAndThen() { //#and-then Future future1 = Futures.successful("value", system.dispatcher()).andThen(new OnComplete() { public void onComplete(Throwable failure, String result) { if (failure != null) sendToIssueTracker(failure); } }).andThen(new OnComplete() { public void onComplete(Throwable failure, String result) { if (result != null) sendToTheInternetz(result); } }); //#and-then } @Test public void useRecover() throws Exception { //#recover Future future = future(new Callable() { public Integer call() { return 1 / 0; } }, system.dispatcher()).recover(new Recover() { public Integer recover(Throwable problem) throws Throwable { if (problem instanceof ArithmeticException) return 0; else throw problem; } }); int result = Await.result(future, Duration.create(1, SECONDS)); assertEquals(result, 0); //#recover } @Test public void useTryRecover() throws Exception { //#try-recover Future future = future(new Callable() { public Integer call() { return 1 / 0; } }, system.dispatcher()).recoverWith(new Recover>() { public Future recover(Throwable problem) throws Throwable { if (problem instanceof ArithmeticException) { return future(new Callable() { public Integer call() { return 0; } }, system.dispatcher()); } else throw problem; } }); int result = Await.result(future, Duration.create(1, SECONDS)); assertEquals(result, 0); //#try-recover } @Test public void useOnSuccessOnFailureAndOnComplete() throws Exception { { Future future = Futures.successful("foo", system.dispatcher()); //#onSuccess future.onSuccess(new OnSuccess() { public void onSuccess(String result) { if ("bar" == result) { //Do something if it resulted in "bar" } else { //Do something if it was some other String } } }); //#onSuccess } { Future future = Futures.failed(new IllegalStateException("OHNOES"), system.dispatcher()); //#onFailure future.onFailure(new OnFailure() { public void onFailure(Throwable failure) { if (failure instanceof IllegalStateException) { //Do something if it was this particular failure } else { //Do something if it was some other failure } } }); //#onFailure } { Future future = Futures.successful("foo", system.dispatcher()); //#onComplete future.onComplete(new OnComplete() { public void onComplete(Throwable failure, String result) { if (failure != null) { //We got a failure, handle it here } else { // We got a result, do something with it } } }); //#onComplete } } @Test public void useOrAndZip() throws Exception { { //#zip Future future1 = Futures.successful("foo", system.dispatcher()); Future future2 = Futures.successful("bar", system.dispatcher()); Future future3 = future1.zip(future2).map(new Mapper, String>() { public String apply(scala.Tuple2 zipped) { return zipped._1() + " " + zipped._2(); } }); String result = Await.result(future3, Duration.create(1, SECONDS)); assertEquals("foo bar", result); //#zip } { //#fallback-to Future future1 = Futures.failed(new IllegalStateException("OHNOES1"), system.dispatcher()); Future future2 = Futures.failed(new IllegalStateException("OHNOES2"), system.dispatcher()); Future future3 = Futures.successful("bar", system.dispatcher()); Future future4 = future1.fallbackTo(future2).fallbackTo(future3); // Will have "bar" in this case String result = Await.result(future4, Duration.create(1, SECONDS)); assertEquals("bar", result); //#fallback-to } } public static class MyActor extends UntypedActor { public void onReceive(Object message) { if (message instanceof String) { getSender().tell(((String) message).toUpperCase()); } else if (message instanceof Integer) { int i = ((Integer) message).intValue(); if (i < 0) { getSender().tell(new Failure(new ArithmeticException("Negative values not supported"))); } else { getSender().tell(i); } } else { unhandled(message); } } } }