Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play 2.0 Akka system shutdown when multiple testcases present

I have two controllers that both use AKKA actors in Play 2.0. Accordingly, there are two test cases which test against these two APIs. However, when execute 'play test', only one of test cases will succeed, the other fails. If I run them separately, it runs successfully. My hunch is the actor system has been shut down by first test. However, I am new to Play 2 and Akka, this is just my guess. Is there a workaround?

@Test
public void callPostA() {
running(testServer(2222, fakeApplication(inMemoryDatabase())), new Runnable() {
        @Override
        public void run() {
            HttpPost httpPost = new HttpPost("http://localhost:2222/controllera");
            ....
        }
    });
}
@Test
public void callPostB() {
running(testServer(2222, fakeApplication(inMemoryDatabase())), new Runnable() {
        @Override
        public void run() {
            HttpPost httpPost = new HttpPost("http://localhost:2222/controllerb");
            ....
        }
    });
}

Two controllers are as follow:

public class PostA extends Controller {
    // master actor for workers
    public static ActorRef masterActorA = Akka.system().actorOf(new Props(new UntypedActorFactory() {
    public UntypedActor create() {
          return new PostAActorMaster(Config.NUMBER_OF_WORKER_ACTOR);
       }
        }), "PostAActorMaster");

    public static Result postA() {

        Map<String, String[]> dict = body.asFormUrlEncoded();
        String paramField1 = dict.get("paramField1");
        String paramField2 = dict.get("paramField2");

        ProductInfo pInfo = new ProductInfo(paramField1, paramField2);
        ProductMessage pMessage = new ProductMessage(pInfo);
        return async(
        Akka.asPromise(ask(masterActorA, pMessage, 15000)).map(
            new Function<Object, Result>() {
                        ...
                        }
                ));
}

public class PostB extends Controller {
    // master actor for workers
    public static ActorRef masterActorB = Akka.system().actorOf(new Props(new UntypedActorFactory() {
    public UntypedActor create() {
          return new PostBActorMaster(Config.NUMBER_OF_WORKER_ACTOR);
       }
        }), "PostBActorMaster");

    public static Result postB() {

        Map<String, String[]> dict = body.asFormUrlEncoded();
        String paramField3 = dict.get("paramField3");
        String paramField4 = dict.get("paramField4");

        BillInfo bInfo = new BillInfo(paramField3, paramField4);
        BillMessage pMessage = new BillMessage(bInfo);
        return async(
        Akka.asPromise(ask(masterActorB, pMessage, 15000)).map(
            new Function<Object, Result>() {
                        ...
                        }
                ));
}

PostA's AKKA Master and worker:

public class PostAActorMaster extends UntypedActor {

    private final ActorRef workerRouter;

    public PostAActorMaster(final int nrOfWorkers) {
        workerRouter = this.getContext().actorOf(new Props(PostAActorMaster.class).withRouter(new RoundRobinRouter(nrOfWorkers)));
    }

    public void onReceive(Object messageObj) {
           try {
            if (messageObj instanceof ProductMessage) {
               // invoke worker to submit channel messaages
               workerRouter.tell(messageObj, getSender());
                } else if (messageObj instanceof ProductMessageResult) {
                    ......
                    getSender().tell("OK");
                }
            } catch (Exception e) {
                ......
            } 
    }

}


public class PostAActorWorker extends UntypedActor {
    public void onReceive(Object messageObj) throws Exception {
              if (messageObj instanceof ProductMessage) {
                     ProductMessage pMessage = (ProductMessage)messageObj;
                     ProductInfo pInfo = pMessage.getProductInfo();
                     log.info(pInfo.getProductId());
                     ProductMessageResult pr = new ProductMessageResult(pInfo);
                 PostA.masterActor.tell(pr, getSender());
              }
        }
}

Managed Object:

public class ProductInfo extends Model {
        @Id
        private String productId;
        ...
   }
like image 290
angelokh Avatar asked Jun 18 '12 20:06

angelokh


2 Answers

I don't see the problem anymore. Here is the structure of my test case. Maybe you can try it and see if it works for you.

Scala:

object LoginApiTest extends PlaySpecification {
  "login api quick login" should {
    "post login data" in new WithCleanTestData {
      var org1 = new OrgInfo("testorg", "Test Inc");
      org1.save();
    }
  }
}


abstract class WithCleanTestData extends WithApplication(FakeApplication(
  additionalConfiguration = TestConf.getConf.toMap
  )) {
  override def around[T: AsResult](t: => T): Result = super.around {
    prepareDbWithData()
    t
  }
  def prepareDbWithData() = {
      OrgInfo.getAllOrgInfo.foreach(_.delete)
  }
}

Java:

public class MyHelpers extends Helpers {
    public static FakeApplication myFakeApplication(Map<String,String> additionalConfiguration) {
        List<String> withoutPlugins = new ArrayList<String>();
        List<String> additionalPlugins = new ArrayList<String>();
        return new MyFakeApplication(new java.io.File("."), MyHelpers.class.getClassLoader(),
            additionalConfiguration, withoutPlugins, additionalPlugins, null);
    }
}

public class BaseModelTest extends WithApplication {
    @Before
    public void before() {
    }
}

public class PostTest extends BaseModelTest {

    @Test
    public void test1() {
    }
}

Also, you can try add this setting to your Build.scala:

parallelExecution in Test := false
like image 125
angelokh Avatar answered Nov 16 '22 03:11

angelokh


Hey I was using a static ActorSystem and this caused a problem

[error] sbt.ForkMain$ForkError: Error creating bean with name 'accountServiceController' defined in file [/Users/admin/Development/src/totes/app/target/scala-2.11/classes/controllers/Ac‌​countServiceController.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [controllers.AccountServiceController]: Constructor threw exception; nested exception is java.lang.IllegalStateException: cannot create children while terminating or terminated

By making a dynamic ActorSystem that is created each time the app starts, the issue appears to go away. I worked on this for a while so I wanted to post this here as a potential solution.

like image 21
JasonG Avatar answered Nov 16 '22 02:11

JasonG