+act #3873 Adding Props.create method with manifested type

The generated classes for Java 8 lambdas doesn't carry the type information that we need, so we need to pass in the return type of the Creator.
This commit is contained in:
Björn Antonsson 2014-02-13 16:16:18 +01:00
parent f1edf78979
commit c432b3e2f0
3 changed files with 59 additions and 9 deletions

View file

@ -54,19 +54,38 @@ public class ActorCreationTest {
return null; return null;
} }
} }
static class G implements Creator {
public Object create() {
return null;
}
}
@Test
@SuppressWarnings("unchecked")
public void testErasedCreator() {
try {
Props.create(new G());
assert false;
} catch(IllegalArgumentException e) {
assertEquals("erased Creator types are unsupported, use Props.create(actorClass, creator) instead", e.getMessage());
}
Props.create(UntypedActor.class, new G());
}
@Test @Test
public void testRightCreator() { public void testRightCreator() {
final Props p = Props.create(new C()); final Props p = Props.create(new C());
assertEquals(UntypedActor.class, p.actorClass()); assertEquals(UntypedActor.class, p.actorClass());
} }
@Test @Test
public void testTopLevelNonStaticCreator() { public void testTopLevelNonStaticCreator() {
final Props p = Props.create(new NonStaticCreator()); final Props p = Props.create(new NonStaticCreator());
assertEquals(UntypedActor.class, p.actorClass()); assertEquals(UntypedActor.class, p.actorClass());
} }
@Test @Test
public void testParametricCreator() { public void testParametricCreator() {
final Props p = Props.create(new D<UntypedActor>()); final Props p = Props.create(new D<UntypedActor>());

View file

@ -57,12 +57,27 @@ public class JavaAPI {
}); });
} }
@SuppressWarnings("unchecked")
public static Props mkErasedProps() {
return Props.create(JavaAPITestActor.class, new Creator() {
public Object create() {
return new JavaAPITestActor();
}
});
}
@Test @Test
public void mustBeAbleToCreateActorRefFromFactory() { public void mustBeAbleToCreateActorRefFromFactory() {
ActorRef ref = system.actorOf(mkProps()); ActorRef ref = system.actorOf(mkProps());
assertNotNull(ref); assertNotNull(ref);
} }
@Test
public void mustBeAbleToCreateActorRefFromErasedFactory() {
ActorRef ref = system.actorOf(mkErasedProps());
assertNotNull(ref);
}
@Test @Test
public void mustBeAbleToCreateActorWIthConstructorParams() { public void mustBeAbleToCreateActorWIthConstructorParams() {
ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", "b", new Integer(17), 18)); ActorRef ref = system.actorOf(Props.create(ActorWithConstructorParams.class, "a", "b", new Integer(17), 18));

View file

@ -100,12 +100,19 @@ object Props {
/** /**
* Create new Props from the given [[akka.japi.Creator]]. * Create new Props from the given [[akka.japi.Creator]].
*
* You can not use a Java 8 lambda with this method since the generated classes
* don't carry enough type information.
*
* Use the Props.create(actorClass, creator) instead.
*/ */
def create[T <: Actor](creator: Creator[T]): Props = { def create[T <: Actor](creator: Creator[T]): Props = {
if ((creator.getClass.getEnclosingClass ne null) && (creator.getClass.getModifiers & Modifier.STATIC) == 0) val cc = creator.getClass
if ((cc.getEnclosingClass ne null) && (cc.getModifiers & Modifier.STATIC) == 0)
throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level") throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level")
val ac = classOf[Actor] val ac = classOf[Actor]
val actorClass = Reflect.findMarker(creator.getClass, classOf[Creator[_]]) match { val coc = classOf[Creator[_]]
val actorClass = Reflect.findMarker(cc, coc) match {
case t: ParameterizedType case t: ParameterizedType
t.getActualTypeArguments.head match { t.getActualTypeArguments.head match {
case c: Class[_] c // since T <: Actor case c: Class[_] c // since T <: Actor
@ -113,9 +120,18 @@ object Props {
v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac c } getOrElse ac v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac c } getOrElse ac
case x throw new IllegalArgumentException(s"unsupported type found in Creator argument [$x]") case x throw new IllegalArgumentException(s"unsupported type found in Creator argument [$x]")
} }
case c: Class[_] if (c == coc)
throw new IllegalArgumentException(s"erased Creator types are unsupported, use Props.create(actorClass, creator) instead")
} }
apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil) apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
} }
/**
* Create new Props from the given [[akka.japi.Creator]] with the type set to the given actorClass.
*/
def create[T <: Actor](actorClass: Class[T], creator: Creator[T]): Props = {
apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
}
} }
/** /**