From 4bbe8f77e8ee111dfb3f4778978df21cd128d118 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Mon, 25 Oct 2010 15:32:54 +0200 Subject: [PATCH 1/2] Closing ticket #471 --- .../src/main/scala/actor/TypedActor.scala | 13 ++++++++++++- .../akka/actor/SimpleJavaPojo.java | 2 ++ .../akka/actor/SimpleJavaPojoImpl.java | 5 +++++ .../scala/actor/typed-actor/TypedActorSpec.scala | 6 ++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/akka-typed-actor/src/main/scala/actor/TypedActor.scala b/akka-typed-actor/src/main/scala/actor/TypedActor.scala index 0aeb127fdc..69e7d57c48 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,12 @@ 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.isDefined) x else JOption.some[AnyRef](null)
+      }
     } 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..8cc029aa89 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 middleName();
   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..219d3806d5 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 middleName() {
+      return Option.some("foo");
+  }
+
   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..ff23a8601c 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,11 @@ class TypedActorSpec extends
       future.result.get should equal (100)
     }
 
+    it("should return none instead of exception") {
+      val middleName = simplePojo.middleName();
+      assert(middleName === Option.some("foo"))
+    }
+
     it("should accept constructor arguments") {
       val pojo1 = TypedActor.newInstance(classOf[MyTypedActor], new MyTypedActorWithConstructorArgsImpl("test", 1L))
       assert(pojo1.sendRequestReply("hello") === "hello test 1")

From e1fa9ebe2d3169c65f818930b1739b9a3a98bd08 Mon Sep 17 00:00:00 2001
From: Viktor Klang 
Date: Mon, 25 Oct 2010 16:12:10 +0200
Subject: [PATCH 2/2] added more tests and fixed corner case to TypedActor
 Option return value

---
 akka-typed-actor/src/main/scala/actor/TypedActor.scala   | 3 ++-
 .../se/scalablesolutions/akka/actor/SimpleJavaPojo.java  | 2 +-
 .../scalablesolutions/akka/actor/SimpleJavaPojoImpl.java | 4 ++--
 .../test/scala/actor/typed-actor/TypedActorSpec.scala    | 9 +++++++--
 4 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/akka-typed-actor/src/main/scala/actor/TypedActor.scala b/akka-typed-actor/src/main/scala/actor/TypedActor.scala
index 69e7d57c48..279c5b4d4e 100644
--- a/akka-typed-actor/src/main/scala/actor/TypedActor.scala
+++ b/akka-typed-actor/src/main/scala/actor/TypedActor.scala
@@ -828,7 +828,8 @@ private[akka] abstract class ActorAspect {
         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.isDefined) x else JOption.some[AnyRef](null)
+        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]
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 8cc029aa89..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
@@ -9,7 +9,7 @@ public interface SimpleJavaPojo {
   public Object getSender();
   public Object getSenderFuture();
   public Future square(int value);
-  public Option middleName();
+  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 219d3806d5..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
@@ -30,8 +30,8 @@ public class SimpleJavaPojoImpl extends TypedActor implements SimpleJavaPojo {
     return getContext().getSenderFuture().get();
   }
 
-  public Option middleName() {
-      return Option.some("foo");
+  public Option passThru(Option returnValue) {
+      return returnValue;
   }
 
   public void setName(String 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 ff23a8601c..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
@@ -81,8 +81,13 @@ class TypedActorSpec extends
     }
 
     it("should return none instead of exception") {
-      val middleName = simplePojo.middleName();
-      assert(middleName === Option.some("foo"))
+      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") {