2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.cluster.raft;
12 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
14 * This program and the accompanying materials are made available under the
15 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
16 * and is available at http://www.eclipse.org/legal/epl-v10.html
19 import akka.actor.Actor;
20 import akka.actor.ActorRef;
21 import akka.actor.ActorSystem;
22 import akka.actor.PoisonPill;
23 import akka.actor.Props;
24 import akka.testkit.JavaTestKit;
25 import akka.testkit.TestActorRef;
26 import akka.util.Timeout;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.concurrent.TimeUnit;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import scala.concurrent.Await;
33 import scala.concurrent.Future;
36 * TestActorFactory provides methods to create both normal and test actors and to kill them when the factory is closed
37 * The ideal usage for TestActorFactory is with try with resources, <br/>
40 * try (TestActorFactory factory = new TestActorFactory(getSystem())){
41 * factory.createActor(props);
42 * factory.createTestActor(props);
43 * factory.generateActorId("leader-");
47 public class TestActorFactory implements AutoCloseable {
48 private final ActorSystem system;
49 List<ActorRef> createdActors = new LinkedList<>();
50 Logger LOG = LoggerFactory.getLogger(getClass());
51 private static int actorCount = 1;
53 public TestActorFactory(ActorSystem system){
58 * Create a normal actor with an auto-generated name
63 public ActorRef createActor(Props props){
64 ActorRef actorRef = system.actorOf(props);
65 return addActor(actorRef);
69 * Create a normal actor with the passed in name
71 * @param actorId name of actor
74 public ActorRef createActor(Props props, String actorId){
75 ActorRef actorRef = system.actorOf(props, actorId);
76 return addActor(actorRef);
80 * Create a test actor with the passed in name
86 @SuppressWarnings("unchecked")
87 public <T extends Actor> TestActorRef<T> createTestActor(Props props, String actorId){
88 TestActorRef<T> actorRef = TestActorRef.create(system, props, actorId);
89 return (TestActorRef<T>) addActor(actorRef);
92 private <T extends ActorRef> ActorRef addActor(T actorRef) {
93 createdActors.add(actorRef);
94 verifyActorReady(actorRef);
98 private void verifyActorReady(ActorRef actorRef) {
99 // Sometimes we see messages go to dead letters soon after creation - it seems the actor isn't quite
100 // in a state yet to receive messages or isn't actually created yet. This seems to happen with
101 // actorSelection so, to alleviate it, we use an actorSelection and call resolveOne with retries to
102 // ensure it's ready.
107 Timeout timeout = new Timeout(100, TimeUnit.MILLISECONDS);
108 Future<ActorRef> future = system.actorSelection(actorRef.path()).resolveOne(timeout);
109 Await.ready(future, timeout.duration());
111 } catch (Exception e) {
113 throw new RuntimeException(e);
120 * Create a test actor with an auto-generated name
125 @SuppressWarnings("unchecked")
126 public <T extends Actor> TestActorRef<T> createTestActor(Props props){
127 TestActorRef<T> actorRef = TestActorRef.create(system, props);
128 return (TestActorRef<T>) addActor(actorRef);
132 * Generate a friendly but unique actor id/name
136 public String generateActorId(String prefix){
137 return prefix + actorCount++;
140 public void killActor(ActorRef actor, JavaTestKit kit) {
141 killActor(actor, kit, true);
144 public String createTestActorPath(String actorId){
145 return "akka://test/user/" + actorId;
148 private void killActor(ActorRef actor, JavaTestKit kit, boolean remove) {
149 LOG.info("Killing actor {}", actor);
151 actor.tell(PoisonPill.getInstance(), ActorRef.noSender());
152 kit.expectTerminated(JavaTestKit.duration("5 seconds"), actor);
155 createdActors.remove(actor);
160 public void close() {
161 JavaTestKit kit = new JavaTestKit(system);
162 for(ActorRef actor : createdActors) {
163 killActor(actor, kit, false);