diff --git a/akka-typed-actor/src/main/scala/actor/TypedActor.scala b/akka-typed-actor/src/main/scala/actor/TypedActor.scala index 0aeb127fdc..279c5b4d4e 100644 --- a/akka-typed-actor/src/main/scala/actor/TypedActor.scala +++ b/akka-typed-actor/src/main/scala/actor/TypedActor.scala @@ -24,6 +24,9 @@ import java.lang.reflect.{Method, Field, InvocationHandler, Proxy => JProxy} * Non-void methods are turned into request-reply messages with the exception of methods returning * a 'Future' which will be sent using request-reply-with-future semantics and need to return the * result using the 'future(..)' method: 'return future(... future result ...);'. + * Methods returning se.scalablesolutions.akka.japi.Option will block until a timeout expires, + * if the implementation of the method returns "none", some(null) will be returned, "none" will only be + * returned when the method didn't respond within the timeout. * * Here is an example of usage (in Java): *
@@ -731,6 +734,9 @@ object TypedActor extends Logging {
   private[akka] def returnsFuture_?(methodRtti: MethodRtti): Boolean =
     classOf[Future[_]].isAssignableFrom(methodRtti.getMethod.getReturnType)
 
+  private[akka] def returnsOption_?(methodRtti: MethodRtti): Boolean =
+    classOf[se.scalablesolutions.akka.japi.Option[_]].isAssignableFrom(methodRtti.getMethod.getReturnType)
+
   private[akka] def supervise(faultHandlingStrategy: FaultHandlingStrategy, components: List[Supervise]): Supervisor =
     Supervisor(SupervisorConfig(faultHandlingStrategy, components))
 
@@ -818,7 +824,13 @@ private[akka] abstract class ActorAspect {
 
     } else if (TypedActor.returnsFuture_?(methodRtti)) {
       actorRef.!!!(joinPoint, timeout)(senderActorRef)
-
+    } else if (TypedActor.returnsOption_?(methodRtti)) {
+        import se.scalablesolutions.akka.japi.{Option => JOption}
+      (actorRef.!!(joinPoint, timeout)(senderActorRef)).as[JOption[AnyRef]] match {
+        case None => JOption.none[AnyRef]
+        case Some(x) if ((x eq null) || x.isEmpty) => JOption.some[AnyRef](null)
+        case Some(x) if(x.isDefined) => x
+      }
     } else {
       val result = (actorRef.!!(joinPoint, timeout)(senderActorRef)).as[AnyRef]
       if (result.isDefined) result.get
diff --git a/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java b/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java
index d3a18abbd9..f4aafa6e1d 100644
--- a/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java
+++ b/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojo.java
@@ -3,11 +3,13 @@ package se.scalablesolutions.akka.actor;
 import se.scalablesolutions.akka.dispatch.Future;
 import se.scalablesolutions.akka.dispatch.CompletableFuture;
 import se.scalablesolutions.akka.dispatch.Future;
+import se.scalablesolutions.akka.japi.Option;
 
 public interface SimpleJavaPojo {
   public Object getSender();
   public Object getSenderFuture();
   public Future square(int value);
+  public Option passThru(Option returnValue);
   public void setName(String name);
   public String getName();
   public void throwException();
diff --git a/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java b/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java
index c02d266ce8..103d84de2d 100644
--- a/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java
+++ b/akka-typed-actor/src/test/java/se/scalablesolutions/akka/actor/SimpleJavaPojoImpl.java
@@ -3,6 +3,7 @@ package se.scalablesolutions.akka.actor;
 import se.scalablesolutions.akka.actor.*;
 import se.scalablesolutions.akka.dispatch.Future;
 import se.scalablesolutions.akka.dispatch.CompletableFuture;
+import se.scalablesolutions.akka.japi.Option;
 
 public class SimpleJavaPojoImpl extends TypedActor implements SimpleJavaPojo {
 
@@ -29,6 +30,10 @@ public class SimpleJavaPojoImpl extends TypedActor implements SimpleJavaPojo {
     return getContext().getSenderFuture().get();
   }
 
+  public Option passThru(Option returnValue) {
+      return returnValue;
+  }
+
   public void setName(String name) {
     this.name = name;
   }
diff --git a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorSpec.scala b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorSpec.scala
index 13c8c8e1fa..219f96f3e2 100644
--- a/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorSpec.scala
+++ b/akka-typed-actor/src/test/scala/actor/typed-actor/TypedActorSpec.scala
@@ -10,6 +10,7 @@ import org.scalatest.matchers.ShouldMatchers
 import org.scalatest.BeforeAndAfterEach
 import org.scalatest.junit.JUnitRunner
 import org.junit.runner.RunWith
+import se.scalablesolutions.akka.japi.Option;
 
 import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture
 import TypedActorSpec._
@@ -79,6 +80,16 @@ class TypedActorSpec extends
       future.result.get should equal (100)
     }
 
+    it("should return none instead of exception") {
+      val someVal = Option.some("foo")
+      val noneVal = Option.none[String]
+      val nullVal = null:Option[String]
+
+      assert(simplePojo.passThru(someVal) === someVal)
+      assert(simplePojo.passThru(noneVal) === Option.some(null))
+      assert(simplePojo.passThru(nullVal) === Option.some(null))
+    }
+
     it("should accept constructor arguments") {
       val pojo1 = TypedActor.newInstance(classOf[MyTypedActor], new MyTypedActorWithConstructorArgsImpl("test", 1L))
       assert(pojo1.sendRequestReply("hello") === "hello test 1")