Handle null constructor parameters, see #3486
This commit is contained in:
parent
a6129e7d31
commit
930c53d3bc
2 changed files with 104 additions and 7 deletions
|
|
@ -12,6 +12,7 @@ import akka.routing.FromConfig;
|
||||||
import akka.routing.NoRouter;
|
import akka.routing.NoRouter;
|
||||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||||
import akka.testkit.AkkaSpec;
|
import akka.testkit.AkkaSpec;
|
||||||
|
import akka.testkit.TestProbe;
|
||||||
|
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -20,8 +21,8 @@ import static org.junit.Assert.*;
|
||||||
public class JavaAPI {
|
public class JavaAPI {
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("JavaAPI",
|
||||||
new AkkaJUnitActorSystemResource("JAvaAPI", AkkaSpec.testConf());
|
AkkaSpec.testConf());
|
||||||
|
|
||||||
private final ActorSystem system = actorSystemResource.getSystem();
|
private final ActorSystem system = actorSystemResource.getSystem();
|
||||||
|
|
||||||
|
|
@ -34,9 +35,9 @@ public class JavaAPI {
|
||||||
|
|
||||||
final LocalScope ls = LocalScope.getInstance();
|
final LocalScope ls = LocalScope.getInstance();
|
||||||
final NoScopeGiven noscope = NoScopeGiven.getInstance();
|
final NoScopeGiven noscope = NoScopeGiven.getInstance();
|
||||||
|
|
||||||
final LoggerInitialized x = Logging.loggerInitialized();
|
final LoggerInitialized x = Logging.loggerInitialized();
|
||||||
|
|
||||||
final CurrentRoutees r = CurrentRoutees.getInstance();
|
final CurrentRoutees r = CurrentRoutees.getInstance();
|
||||||
final NoRouter nr = NoRouter.getInstance();
|
final NoRouter nr = NoRouter.getInstance();
|
||||||
final FromConfig fc = FromConfig.getInstance();
|
final FromConfig fc = FromConfig.getInstance();
|
||||||
|
|
@ -57,4 +58,95 @@ public class JavaAPI {
|
||||||
}));
|
}));
|
||||||
assertNotNull(ref);
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ private[akka] object Reflect {
|
||||||
try constructor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*)
|
try constructor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*)
|
||||||
catch {
|
catch {
|
||||||
case e: IllegalArgumentException ⇒
|
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)
|
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] = {
|
private[akka] def findConstructor[T](clazz: Class[T], args: immutable.Seq[Any]): Constructor[T] = {
|
||||||
def error(msg: String): Nothing = {
|
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]")
|
throw new IllegalArgumentException(s"$msg found on $clazz for arguments [$argClasses]")
|
||||||
}
|
}
|
||||||
val candidates =
|
val candidates =
|
||||||
clazz.getDeclaredConstructors filter (c ⇒
|
clazz.getDeclaredConstructors filter (c ⇒
|
||||||
c.getParameterTypes.length == args.length &&
|
c.getParameterTypes.length == args.length &&
|
||||||
(c.getParameterTypes zip args forall {
|
(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]]
|
if (candidates.size == 1) candidates.head.asInstanceOf[Constructor[T]]
|
||||||
else if (candidates.size > 1) error("multiple matching constructors")
|
else if (candidates.size > 1) error("multiple matching constructors")
|
||||||
else error("no matching constructor")
|
else error("no matching constructor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def safeGetClass(a: Any): Class[_] =
|
||||||
|
if (a == null) classOf[AnyRef] else a.getClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
* @param clazz the class which to instantiate an instance of
|
* @param clazz the class which to instantiate an instance of
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue