From 930c53d3bcf398a73cc23cf0802ee6af3e617eff Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 3 Jul 2013 09:47:22 +0200 Subject: [PATCH] Handle null constructor parameters, see #3486 --- .../src/test/java/akka/actor/JavaAPI.java | 100 +++++++++++++++++- .../src/main/scala/akka/util/Reflect.scala | 11 +- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java b/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java index 9abce884bf..d834dea1ca 100644 --- a/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java +++ b/akka-actor-tests/src/test/java/akka/actor/JavaAPI.java @@ -12,6 +12,7 @@ import akka.routing.FromConfig; import akka.routing.NoRouter; import akka.testkit.AkkaJUnitActorSystemResource; import akka.testkit.AkkaSpec; +import akka.testkit.TestProbe; import org.junit.ClassRule; import org.junit.Test; @@ -20,8 +21,8 @@ import static org.junit.Assert.*; public class JavaAPI { @ClassRule - public static AkkaJUnitActorSystemResource actorSystemResource = - new AkkaJUnitActorSystemResource("JAvaAPI", AkkaSpec.testConf()); + public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("JavaAPI", + AkkaSpec.testConf()); private final ActorSystem system = actorSystemResource.getSystem(); @@ -34,9 +35,9 @@ public class JavaAPI { final LocalScope ls = LocalScope.getInstance(); final NoScopeGiven noscope = NoScopeGiven.getInstance(); - + final LoggerInitialized x = Logging.loggerInitialized(); - + final CurrentRoutees r = CurrentRoutees.getInstance(); final NoRouter nr = NoRouter.getInstance(); final FromConfig fc = FromConfig.getInstance(); @@ -57,4 +58,95 @@ public class JavaAPI { })); assertNotNull(ref); } + + @Test + public void mustBeAbleToCreateActorWIthConstructorParams() { + ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", "b", new Integer(17), 18)); + final TestProbe probe = new TestProbe(system); + probe.send(ref, "get"); + probe.expectMsg("a-b-17-18"); + } + + @Test + public void mustBeAbleToCreateActorWIthBoxedAndUnBoxedConstructorParams() { + ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", "b", 17, new Integer(18))); + final TestProbe probe = new TestProbe(system); + probe.send(ref, "get"); + probe.expectMsg("a-b-17-18"); + } + + @Test + public void mustBeAbleToCreateActorWIthNullConstructorParams() { + ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", null, null, 18)); + final TestProbe probe = new TestProbe(system); + probe.send(ref, "get"); + probe.expectMsg("a-null-null-18"); + } + + @Test + public void mustBeAbleToCreateActorWIthNullConstructorParams2() { + // without this Object array wrapper it will not compile: "reference to create is ambiguous" + ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, new Object[] { null })); + final TestProbe probe = new TestProbe(system); + probe.send(ref, "get"); + probe.expectMsg("null-undefined-0-0"); + } + + @Test + public void mustBeAbleToCreateActorWIthNullConstructorParams3() { + ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", null)); + final TestProbe probe = new TestProbe(system); + probe.send(ref, "get"); + probe.expectMsg("a-null-0-0"); + } + + public static class ActorWithConstructorParams extends UntypedActor { + + private final String a; + private final String b; + private final Integer c; + private final int d; + + public ActorWithConstructorParams(String a, String b, Integer c, int d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + public ActorWithConstructorParams(String a, Object b) { + this.a = a; + this.b = String.valueOf(b); + this.c = 0; + this.d = 0; + } + + public ActorWithConstructorParams(String a, int d) { + this.a = a; + this.b = "undefined"; + this.c = 0; + this.d = d; + } + + public ActorWithConstructorParams(Object a) { + this.a = String.valueOf(a); + this.b = "undefined"; + this.c = 0; + this.d = 0; + } + + public ActorWithConstructorParams(int d) { + this.a = "undefined"; + this.b = "undefined"; + this.c = 0; + this.d = d; + } + + @Override + public void onReceive(Object msg) { + String reply = String.valueOf(a) + "-" + String.valueOf(b) + "-" + String.valueOf(c) + "-" + String.valueOf(d); + getSender().tell(reply, getSelf()); + } + } + } diff --git a/akka-actor/src/main/scala/akka/util/Reflect.scala b/akka-actor/src/main/scala/akka/util/Reflect.scala index 4e67044bb0..3a92d4f114 100644 --- a/akka-actor/src/main/scala/akka/util/Reflect.scala +++ b/akka-actor/src/main/scala/akka/util/Reflect.scala @@ -65,7 +65,7 @@ private[akka] object Reflect { try constructor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*) catch { case e: IllegalArgumentException ⇒ - val argString = args map (_.getClass) mkString ("[", ", ", "]") + val argString = args map safeGetClass mkString ("[", ", ", "]") throw new IllegalArgumentException(s"constructor $constructor is incompatible with arguments $argString", e) } } @@ -77,20 +77,25 @@ private[akka] object Reflect { */ private[akka] def findConstructor[T](clazz: Class[T], args: immutable.Seq[Any]): Constructor[T] = { def error(msg: String): Nothing = { - val argClasses = args map (_.getClass) mkString ", " + val argClasses = args map safeGetClass mkString ", " throw new IllegalArgumentException(s"$msg found on $clazz for arguments [$argClasses]") } val candidates = clazz.getDeclaredConstructors filter (c ⇒ c.getParameterTypes.length == args.length && (c.getParameterTypes zip args forall { - case (found, required) ⇒ found.isInstance(required) || BoxedType(found).isInstance(required) + case (found, required) ⇒ + found.isInstance(required) || BoxedType(found).isInstance(required) || + (required == null && !found.isPrimitive) })) if (candidates.size == 1) candidates.head.asInstanceOf[Constructor[T]] else if (candidates.size > 1) error("multiple matching constructors") else error("no matching constructor") } + private def safeGetClass(a: Any): Class[_] = + if (a == null) classOf[AnyRef] else a.getClass + /** * INTERNAL API * @param clazz the class which to instantiate an instance of