splitted up akka-core into three modules; akka-actors, akka-typed-actors, akka-core
This commit is contained in:
parent
b7b79484ba
commit
c67b17a912
149 changed files with 11195 additions and 1399 deletions
|
|
@ -1,113 +0,0 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<name>Akka TypedActor Tests in Java</name>
|
||||
<artifactId>akka-active-object-test</artifactId>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<version>0.9</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<scala.version>2.8.0.RC3</scala.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>embedded-repo</id>
|
||||
<name>Embedded Repository</name>
|
||||
<url>file:///Users/jboner/src/scala/akka/embedded-repo</url>
|
||||
<snapshots />
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<name>JBoss Repository</name>
|
||||
<url>https://repository.jboss.org/nexus/content/groups/public</url>
|
||||
<snapshots />
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>scala-tools.org</id>
|
||||
<name>Scala-Tools Maven2 Repository</name>
|
||||
<url>http://scala-tools.org/repo-releases</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<artifactId>akka-core_2.8.0.RC3</artifactId>
|
||||
<version>0.9.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.multiverse</groupId>
|
||||
<artifactId>multiverse-alpha</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jmock</groupId>
|
||||
<artifactId>jmock</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.multiverse</groupId>
|
||||
<artifactId>multiverse-alpha</artifactId>
|
||||
<version>0.6-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/*Persistent*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>src/test/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>src/test/java</directory>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
public class AllTest extends TestCase {
|
||||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("All Java tests");
|
||||
suite.addTestSuite(InMemoryStateTest.class);
|
||||
suite.addTestSuite(InMemNestedStateTest.class);
|
||||
suite.addTestSuite(RemoteInMemoryStateTest.class);
|
||||
suite.addTestSuite(TypedActorGuiceConfiguratorTest.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(suite());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
public class InMemFailer implements java.io.Serializable {
|
||||
public int fail() {
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import se.scalablesolutions.akka.config.*;
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class InMemNestedStateTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
public InMemNestedStateTest() {
|
||||
conf.configure(
|
||||
new RestartStrategy(new AllForOne(), 3, 5000, new Class[]{Exception.class}),
|
||||
new Component[]{
|
||||
new Component(InMemStateful.class, new LifeCycle(new Permanent()), 10000000),
|
||||
new Component(InMemStatefulNested.class, new LifeCycle(new Permanent()), 10000000),
|
||||
new Component(InMemFailer.class, new LifeCycle(new Permanent()), 1000)
|
||||
//new Component("inmem-clasher", InMemClasher.class, InMemClasherImpl.class, new LifeCycle(new Permanent()), 100000)
|
||||
}).supervise();
|
||||
Config.config();
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.init();
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
nested.init();
|
||||
}
|
||||
|
||||
public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() throws Exception {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
nested.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state
|
||||
Thread.sleep(100);
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested); // transactionrequired
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess"));
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", nested.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess"));
|
||||
}
|
||||
|
||||
public void testMapShouldRollbackStateForStatefulServerInCaseOfFailure() throws InterruptedException {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
nested.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer); // call failing transactionrequired method
|
||||
Thread.sleep(100);
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
Thread.sleep(100);
|
||||
assertEquals("init", nested.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess() throws Exception {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setVectorState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
Thread.sleep(100);
|
||||
nested.setVectorState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested); // transactionrequired
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", stateful.getVectorState());
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", nested.getVectorState());
|
||||
}
|
||||
|
||||
public void testVectorShouldRollbackStateForStatefulServerInCaseOfFailure() throws InterruptedException {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setVectorState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
nested.setVectorState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer); // call failing transactionrequired method
|
||||
Thread.sleep(100);
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getVectorState()); // check that state is == init state
|
||||
Thread.sleep(100);
|
||||
assertEquals("init", nested.getVectorState()); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess() throws Exception {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
stateful.setRefState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
nested.setRefState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested); // transactionrequired
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", stateful.getRefState());
|
||||
Thread.sleep(100);
|
||||
assertEquals("new state", nested.getRefState());
|
||||
}
|
||||
|
||||
public void testRefShouldRollbackStateForStatefulServerInCaseOfFailure() throws InterruptedException {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
InMemStatefulNested nested = conf.getInstance(InMemStatefulNested.class);
|
||||
stateful.setRefState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
nested.setRefState("init"); // set init state
|
||||
Thread.sleep(100);
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer); // call failing transactionrequired method
|
||||
Thread.sleep(100);
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getRefState()); // check that state is == init state
|
||||
Thread.sleep(100);
|
||||
assertEquals("init", nested.getRefState()); // check that state is == init state
|
||||
}
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.*;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class InMemoryStateTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
public InMemoryStateTest() {
|
||||
Config.config();
|
||||
conf.configure(
|
||||
new RestartStrategy(new AllForOne(), 3, 5000, new Class[] {Exception.class}),
|
||||
new Component[]{
|
||||
new Component(InMemStateful.class,
|
||||
new LifeCycle(new Permanent()),
|
||||
//new RestartCallbacks("preRestart", "postRestart")),
|
||||
10000),
|
||||
new Component(InMemFailer.class,
|
||||
new LifeCycle(new Permanent()),
|
||||
10000)
|
||||
}).supervise();
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.init();
|
||||
}
|
||||
|
||||
public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess"));
|
||||
}
|
||||
|
||||
public void testMapShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setVectorState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getVectorState());
|
||||
}
|
||||
|
||||
public void testVectorShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setVectorState("init"); // set init state
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getVectorState()); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setRefState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getRefState());
|
||||
}
|
||||
|
||||
public void testRefShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setRefState("init"); // set init state
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getRefState()); // check that state is == init state
|
||||
}
|
||||
/*
|
||||
public void testNestedNonTransactionalMethodHangs() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.thisMethodHangs("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
}
|
||||
*/
|
||||
// public void testShouldRollbackStateForStatefulServerInCaseOfMessageClash()
|
||||
// {
|
||||
// InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
// stateful.setState("stateful", "init"); // set init state
|
||||
//
|
||||
// InMemClasher clasher = conf.getInstance(InMemClasher.class);
|
||||
// clasher.setState("clasher", "init"); // set init state
|
||||
//
|
||||
// // try {
|
||||
// // stateful.clashOk("stateful", "new state", clasher);
|
||||
// // } catch (RuntimeException e) { } // expected
|
||||
// // assertEquals("new state", stateful.getState("stateful")); // check that
|
||||
// // state is == init state
|
||||
// // assertEquals("was here", clasher.getState("clasher")); // check that
|
||||
// // state is == init state
|
||||
//
|
||||
// try {
|
||||
// stateful.clashNotOk("stateful", "new state", clasher);
|
||||
// fail("should have thrown an exception");
|
||||
// } catch (RuntimeException e) {
|
||||
// } // expected
|
||||
// assertEquals("init", stateful.getState("stateful")); // check that state is
|
||||
// // == init state
|
||||
// // assertEquals("init", clasher.getState("clasher")); // check that state
|
||||
// is
|
||||
// // == init state
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
interface InMemClasher {
|
||||
public void clash();
|
||||
|
||||
public String getState(String key);
|
||||
|
||||
public void setState(String key, String value);
|
||||
}
|
||||
|
||||
class InMemClasherImpl implements InMemClasher {
|
||||
@state
|
||||
private TransactionalMap<String, Object> state = new InMemoryTransactionalMap<String, Object>();
|
||||
|
||||
public String getState(String key) {
|
||||
return (String) state.get(key).get();
|
||||
}
|
||||
|
||||
public void setState(String key, String msg) {
|
||||
state.put(key, msg);
|
||||
}
|
||||
|
||||
public void clash() {
|
||||
state.put("clasher", "was here");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import static se.scalablesolutions.akka.actor.TypedActor.link;
|
||||
import static se.scalablesolutions.akka.actor.TypedActor.newInstance;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import se.scalablesolutions.akka.config.OneForOneStrategy;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* <p>Small misc tests that do not fit anywhere else and does not require a separate testcase</p>
|
||||
*
|
||||
* @author johanrask
|
||||
*
|
||||
*/
|
||||
public class MiscTypedActorTest extends TestCase {
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that both preRestart and postRestart methods are invoked when
|
||||
* an actor is restarted
|
||||
*/
|
||||
public void testFailingPostRestartInvocation() throws InterruptedException {
|
||||
SimpleJavaPojo pojo = newInstance(SimpleJavaPojo.class,500);
|
||||
SimpleJavaPojo supervisor = newInstance(SimpleJavaPojo.class,500);
|
||||
link(supervisor,pojo,new OneForOneStrategy(3, 2000),new Class[]{Throwable.class});
|
||||
pojo.throwException();
|
||||
Thread.sleep(500);
|
||||
Assert.assertTrue(pojo.pre);
|
||||
Assert.assertTrue(pojo.post);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import se.scalablesolutions.akka.remote.RemoteNode;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class RemoteInMemoryStateTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
static {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
RemoteNode.start();
|
||||
}
|
||||
}).start();
|
||||
try { Thread.currentThread().sleep(1000); } catch (Exception e) {}
|
||||
Config.config();
|
||||
}
|
||||
final TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
protected void tearDown() {
|
||||
conf.stop();
|
||||
}
|
||||
|
||||
public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 1000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess"));
|
||||
}
|
||||
|
||||
public void testMapShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 1000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setVectorState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getVectorState());
|
||||
}
|
||||
|
||||
public void testVectorShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setVectorState("init"); // set init state
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getVectorState()); // check that state is == init state
|
||||
}
|
||||
|
||||
public void testRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setRefState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
assertEquals("new state", stateful.getRefState());
|
||||
}
|
||||
|
||||
public void testRefShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setRefState("init"); // set init state
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getRefState()); // check that state is == init state
|
||||
}
|
||||
/*
|
||||
public void testNestedNonTransactionalMethodHangs() {
|
||||
InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
InMemFailer failer = conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.thisMethodHangs("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
} catch (RuntimeException e) {
|
||||
} // expected
|
||||
assertEquals("init", stateful.getMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")); // check that state is == init state
|
||||
}
|
||||
*/
|
||||
// public void testShouldRollbackStateForStatefulServerInCaseOfMessageClash()
|
||||
// {
|
||||
// InMemStateful stateful = conf.getInstance(InMemStateful.class);
|
||||
// stateful.setState("stateful", "init"); // set init state
|
||||
//
|
||||
// InMemClasher clasher = conf.getInstance(InMemClasher.class);
|
||||
// clasher.setState("clasher", "init"); // set init state
|
||||
//
|
||||
// // try {
|
||||
// // stateful.clashOk("stateful", "new state", clasher);
|
||||
// // } catch (RuntimeException e) { } // expected
|
||||
// // assertEquals("new state", stateful.getState("stateful")); // check that
|
||||
// // state is == init state
|
||||
// // assertEquals("was here", clasher.getState("clasher")); // check that
|
||||
// // state is == init state
|
||||
//
|
||||
// try {
|
||||
// stateful.clashNotOk("stateful", "new state", clasher);
|
||||
// fail("should have thrown an exception");
|
||||
// } catch (RuntimeException e) {
|
||||
// } // expected
|
||||
// assertEquals("init", stateful.getState("stateful")); // check that state is
|
||||
// // == init state
|
||||
// // assertEquals("init", clasher.getState("clasher")); // check that state
|
||||
// is
|
||||
// // == init state
|
||||
// }
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
|
||||
public class SimpleJavaPojo {
|
||||
|
||||
public boolean pre = false;
|
||||
public boolean post = false;
|
||||
|
||||
private String name;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void pre() {
|
||||
System.out.println("** pre()");
|
||||
pre = true;
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void post() {
|
||||
System.out.println("** post()");
|
||||
post = true;
|
||||
}
|
||||
|
||||
public void throwException() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Scopes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
import se.scalablesolutions.akka.dispatch.*;
|
||||
|
||||
public class TypedActorGuiceConfiguratorTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
protected void setUp() {
|
||||
Config.config();
|
||||
MessageDispatcher dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("test");
|
||||
|
||||
conf.addExternalGuiceModule(new AbstractModule() {
|
||||
protected void configure() {
|
||||
bind(Ext.class).to(ExtImpl.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}).configure(
|
||||
new RestartStrategy(new AllForOne(), 3, 5000, new Class[]{Exception.class}),
|
||||
new Component[]{
|
||||
new Component(
|
||||
Foo.class,
|
||||
new LifeCycle(new Permanent()),
|
||||
1000,
|
||||
dispatcher),
|
||||
//new RemoteAddress("localhost", 9999)),
|
||||
new Component(
|
||||
Bar.class,
|
||||
BarImpl.class,
|
||||
new LifeCycle(new Permanent()),
|
||||
1000,
|
||||
dispatcher)
|
||||
}).inject().supervise();
|
||||
|
||||
}
|
||||
|
||||
public void testGuiceTypedActorInjection() {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
Bar bar = conf.getInstance(Bar.class);
|
||||
assertEquals(foo.getBar(), bar);
|
||||
}
|
||||
|
||||
public void testGuiceExternalDependencyInjection() {
|
||||
messageLog = "";
|
||||
Bar bar = conf.getInstance(Bar.class);
|
||||
Ext ext = conf.getExternalDependency(Ext.class);
|
||||
assertTrue(bar.getExt().toString().equals(ext.toString()));
|
||||
}
|
||||
|
||||
public void testLookupNonSupervisedInstance() {
|
||||
try {
|
||||
String str = conf.getInstance(String.class);
|
||||
fail("exception should have been thrown");
|
||||
} catch (Exception e) {
|
||||
assertEquals(IllegalStateException.class, e.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public void testTypedActorInvocation() throws InterruptedException {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
messageLog += foo.foo("foo ");
|
||||
foo.bar("bar ");
|
||||
messageLog += "before_bar ";
|
||||
Thread.sleep(500);
|
||||
assertEquals("foo return_foo before_bar ", messageLog);
|
||||
}
|
||||
|
||||
public void testTypedActorInvocationsInvocation() throws InterruptedException {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
Bar bar = conf.getInstance(Bar.class);
|
||||
messageLog += foo.foo("foo ");
|
||||
foo.bar("bar ");
|
||||
messageLog += "before_bar ";
|
||||
Thread.sleep(500);
|
||||
assertEquals("foo return_foo before_bar ", messageLog);
|
||||
}
|
||||
|
||||
|
||||
public void testForcedTimeout() {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
try {
|
||||
foo.longRunning();
|
||||
fail("exception should have been thrown");
|
||||
} catch (se.scalablesolutions.akka.dispatch.FutureTimeoutException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testForcedException() {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
try {
|
||||
foo.throwsException();
|
||||
fail("exception should have been thrown");
|
||||
} catch (RuntimeException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -7,7 +7,6 @@ package se.scalablesolutions.akka.actor
|
|||
import se.scalablesolutions.akka.dispatch._
|
||||
import se.scalablesolutions.akka.config.Config._
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.serialization.Serializer
|
||||
import se.scalablesolutions.akka.util.Helpers.{narrow, narrowSilently}
|
||||
import se.scalablesolutions.akka.util.{Logging, Duration}
|
||||
import se.scalablesolutions.akka.AkkaException
|
||||
|
|
@ -502,3 +501,7 @@ private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) {
|
|||
*/
|
||||
def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption)
|
||||
}
|
||||
|
||||
trait Proxyable {
|
||||
def swapProxiedActor(newInstance: Actor)
|
||||
}
|
||||
|
|
@ -5,18 +5,15 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.dispatch._
|
||||
import se.scalablesolutions.akka.config.Config.config
|
||||
import se.scalablesolutions.akka.config.Config._
|
||||
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.stm.global._
|
||||
import se.scalablesolutions.akka.stm.TransactionManagement._
|
||||
import se.scalablesolutions.akka.stm.{TransactionManagement, TransactionSetAbortedException}
|
||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
||||
import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient, MessageSerializer, RemoteRequestProtocolIdFactory}
|
||||
import se.scalablesolutions.akka.serialization.{Serializer, BinaryString}
|
||||
import se.scalablesolutions.akka.util.{HashCode, Logging, UUID, ReentrantGuard}
|
||||
import se.scalablesolutions.akka.remote.{RemoteClientModule, RemoteServerModule}
|
||||
import se.scalablesolutions.akka.AkkaException
|
||||
import RemoteActorSerialization._
|
||||
|
||||
import org.multiverse.api.ThreadLocalTransaction._
|
||||
import org.multiverse.commitbarriers.CountDownCommitBarrier
|
||||
|
|
@ -33,8 +30,6 @@ import java.lang.reflect.Field
|
|||
|
||||
import scala.reflect.BeanProperty
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
|
||||
/**
|
||||
* ActorRef is an immutable and serializable handle to an Actor.
|
||||
* <p/>
|
||||
|
|
@ -67,15 +62,18 @@ import com.google.protobuf.ByteString
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
trait ActorRef extends ActorRefShared with TransactionManagement with java.lang.Comparable[ActorRef] {
|
||||
scalaRef: ScalaActorRef =>
|
||||
trait ActorRef extends
|
||||
ActorRefShared with
|
||||
TransactionManagement with
|
||||
Logging with
|
||||
java.lang.Comparable[ActorRef] { scalaRef: ScalaActorRef =>
|
||||
|
||||
// Only mutable for RemoteServer in order to maintain identity across nodes
|
||||
@volatile protected[akka] var _uuid = UUID.newUuid.toString
|
||||
@volatile protected[this] var _isRunning = false
|
||||
@volatile protected[this] var _isShutDown = false
|
||||
@volatile protected[akka] var _isBeingRestarted = false
|
||||
@volatile protected[akka] var _homeAddress = new InetSocketAddress(RemoteServer.HOSTNAME, RemoteServer.PORT)
|
||||
@volatile protected[akka] var _homeAddress = new InetSocketAddress(RemoteServerModule.HOSTNAME, RemoteServerModule.PORT)
|
||||
@volatile protected[akka] var _futureTimeout: Option[ScheduledFuture[AnyRef]] = None
|
||||
@volatile protected[akka] var startOnCreation = false
|
||||
@volatile protected[akka] var registeredInRemoteNodeDuringSerialization = false
|
||||
|
|
@ -681,13 +679,11 @@ class LocalActorRef private[akka](
|
|||
if (runActorInitialization && !isDeserialized) initializeActorInstance
|
||||
|
||||
private[akka] def this(clazz: Class[_ <: Actor]) = this(Left(Some(clazz)))
|
||||
private[akka] def this(factory: () => Actor) = this(Right(Some(factory)))
|
||||
private[akka] def this(factory: () => Actor) = this(Right(Some(factory)))
|
||||
|
||||
// used only for deserialization
|
||||
private[akka] def this(__uuid: String,
|
||||
__id: String,
|
||||
__actorClassName: String,
|
||||
__actorBytes: Array[Byte],
|
||||
__hostname: String,
|
||||
__port: Int,
|
||||
__isTransactor: Boolean,
|
||||
|
|
@ -697,16 +693,8 @@ class LocalActorRef private[akka](
|
|||
__supervisor: Option[ActorRef],
|
||||
__hotswap: Option[PartialFunction[Any, Unit]],
|
||||
__loader: ClassLoader,
|
||||
__messages: List[RemoteRequestProtocol],
|
||||
__format: Format[_ <: Actor]) = {
|
||||
this(() => {
|
||||
val actorClass = __loader.loadClass(__actorClassName)
|
||||
if (__format.isInstanceOf[SerializerBasedActorFormat[_]])
|
||||
__format.asInstanceOf[SerializerBasedActorFormat[_]]
|
||||
.serializer
|
||||
.fromBinary(__actorBytes, Some(actorClass)).asInstanceOf[Actor]
|
||||
else actorClass.newInstance.asInstanceOf[Actor]
|
||||
})
|
||||
__factory: () => Actor) = {
|
||||
this(__factory)
|
||||
loader = Some(__loader)
|
||||
isDeserialized = true
|
||||
_uuid = __uuid
|
||||
|
|
@ -721,7 +709,6 @@ class LocalActorRef private[akka](
|
|||
actorSelfFields._1.set(actor, this)
|
||||
actorSelfFields._2.set(actor, Some(this))
|
||||
start
|
||||
__messages.foreach(message => this ! MessageSerializer.deserialize(message.getMessage))
|
||||
checkReceiveTimeout
|
||||
ActorRegistry.register(this)
|
||||
}
|
||||
|
|
@ -755,19 +742,22 @@ class LocalActorRef private[akka](
|
|||
/**
|
||||
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
|
||||
*/
|
||||
def makeRemote(hostname: String, port: Int): Unit =
|
||||
def makeRemote(hostname: String, port: Int): Unit = {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
if (!isRunning || isBeingRestarted) makeRemote(new InetSocketAddress(hostname, port))
|
||||
else throw new ActorInitializationException(
|
||||
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
|
||||
*/
|
||||
def makeRemote(address: InetSocketAddress): Unit = guard.withGuard {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
if (!isRunning || isBeingRestarted) {
|
||||
_remoteAddress = Some(address)
|
||||
RemoteClient.register(address.getHostName, address.getPort, uuid)
|
||||
homeAddress = (RemoteServer.HOSTNAME, RemoteServer.PORT)
|
||||
RemoteClientModule.register(address, uuid)
|
||||
homeAddress = (RemoteServerModule.HOSTNAME, RemoteServerModule.PORT)
|
||||
} else throw new ActorInitializationException(
|
||||
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
|
||||
}
|
||||
|
|
@ -839,9 +829,10 @@ class LocalActorRef private[akka](
|
|||
_isShutDown = true
|
||||
actor.shutdown
|
||||
ActorRegistry.unregister(this)
|
||||
remoteAddress.foreach(address => RemoteClient.unregister(
|
||||
address.getHostName, address.getPort, uuid))
|
||||
RemoteNode.unregister(this)
|
||||
remoteAddress.foreach { address =>
|
||||
RemoteClientModule.unregister(address, uuid)
|
||||
}
|
||||
RemoteClientModule.unregister(this)
|
||||
nullOutActorRefReferencesFor(actorInstance.get)
|
||||
} //else if (isBeingRestarted) throw new ActorKilledException("Actor [" + toString + "] is being restarted.")
|
||||
}
|
||||
|
|
@ -896,6 +887,7 @@ class LocalActorRef private[akka](
|
|||
* To be invoked from within the actor itself.
|
||||
*/
|
||||
def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int) = guard.withGuard {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
try {
|
||||
actorRef.makeRemote(hostname, port)
|
||||
actorRef.start
|
||||
|
|
@ -921,6 +913,7 @@ class LocalActorRef private[akka](
|
|||
* To be invoked from within the actor itself.
|
||||
*/
|
||||
def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
val actor = spawnButDoNotStart(clazz)
|
||||
actor.makeRemote(hostname, port)
|
||||
actor.start
|
||||
|
|
@ -948,6 +941,7 @@ class LocalActorRef private[akka](
|
|||
* To be invoked from within the actor itself.
|
||||
*/
|
||||
def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
val actor = spawnButDoNotStart(clazz)
|
||||
try {
|
||||
actor.makeRemote(hostname, port)
|
||||
|
|
@ -984,10 +978,8 @@ class LocalActorRef private[akka](
|
|||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
|
||||
joinTransaction(message)
|
||||
|
||||
if (remoteAddress.isDefined) {
|
||||
RemoteClient.clientFor(remoteAddress.get).send[Any](
|
||||
createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
|
||||
} else {
|
||||
if (remoteAddress.isDefined) RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
|
||||
else {
|
||||
val invocation = new MessageInvocation(this, message, senderOption, None, transactionSet.get)
|
||||
invocation.send
|
||||
}
|
||||
|
|
@ -1000,12 +992,9 @@ class LocalActorRef private[akka](
|
|||
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
|
||||
joinTransaction(message)
|
||||
|
||||
if (remoteAddress.isDefined) {
|
||||
val future = RemoteClient.clientFor(remoteAddress.get).send(
|
||||
createRemoteRequestProtocolBuilder(this, message, false, senderOption).build, senderFuture)
|
||||
if (future.isDefined) future.get
|
||||
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
|
||||
} else {
|
||||
if (remoteAddress.isDefined) RemoteClientModule.send(
|
||||
message, senderOption, senderFuture, remoteAddress.get, this)
|
||||
else {
|
||||
val future = if (senderFuture.isDefined) senderFuture.get
|
||||
else new DefaultCompletableFuture[T](timeout)
|
||||
val invocation = new MessageInvocation(
|
||||
|
|
@ -1088,7 +1077,7 @@ class LocalActorRef private[akka](
|
|||
Actor.log.debug("Restarting linked actors for actor [%s].", id)
|
||||
restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
|
||||
Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id)
|
||||
if (isTypedActorDispatcher(failedActor)) restartTypedActorDispatcher(failedActor, reason)
|
||||
if (isProxyableDispatcher(failedActor)) restartProxyableDispatcher(failedActor, reason)
|
||||
else restartActor(failedActor, reason)
|
||||
_isBeingRestarted = false
|
||||
}
|
||||
|
|
@ -1107,8 +1096,9 @@ class LocalActorRef private[akka](
|
|||
}
|
||||
|
||||
protected[akka] def registerSupervisorAsRemoteActor: Option[String] = guard.withGuard {
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
if (_supervisor.isDefined) {
|
||||
RemoteClient.clientFor(remoteAddress.get).registerSupervisorForActor(this)
|
||||
remoteAddress.foreach(address => RemoteClientModule.registerSupervisorForActor(address, this))
|
||||
Some(_supervisor.get.uuid)
|
||||
} else None
|
||||
}
|
||||
|
|
@ -1126,9 +1116,9 @@ class LocalActorRef private[akka](
|
|||
|
||||
// ========= PRIVATE FUNCTIONS =========
|
||||
|
||||
private def isTypedActorDispatcher(a: Actor): Boolean = a.isInstanceOf[TypedActor]
|
||||
private def isProxyableDispatcher(a: Actor): Boolean = a.isInstanceOf[Proxyable]
|
||||
|
||||
private def restartTypedActorDispatcher(failedActor: Actor, reason: Throwable) = {
|
||||
private def restartProxyableDispatcher(failedActor: Actor, reason: Throwable) = {
|
||||
failedActor.preRestart(reason)
|
||||
failedActor.postRestart(reason)
|
||||
}
|
||||
|
|
@ -1140,7 +1130,8 @@ class LocalActorRef private[akka](
|
|||
freshActor.init
|
||||
freshActor.initTransactionalState
|
||||
actorInstance.set(freshActor)
|
||||
if (failedActor.isInstanceOf[TypedActor]) failedActor.asInstanceOf[TypedActor].swapInstanceInProxy(freshActor)
|
||||
if (failedActor.isInstanceOf[Proxyable])
|
||||
failedActor.asInstanceOf[Proxyable].swapProxiedActor(freshActor)
|
||||
Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id)
|
||||
freshActor.postRestart(reason)
|
||||
}
|
||||
|
|
@ -1316,6 +1307,7 @@ class LocalActorRef private[akka](
|
|||
checkReceiveTimeout
|
||||
}
|
||||
|
||||
/*
|
||||
private def serializeMessage(message: AnyRef): AnyRef = if (Actor.SERIALIZE_MESSAGES) {
|
||||
if (!message.isInstanceOf[String] &&
|
||||
!message.isInstanceOf[Byte] &&
|
||||
|
|
@ -1339,6 +1331,7 @@ class LocalActorRef private[akka](
|
|||
Serializer.Java.deepClone(message)
|
||||
} else message
|
||||
} else message
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1347,7 +1340,7 @@ class LocalActorRef private[akka](
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object RemoteActorSystemMessage {
|
||||
val Stop = BinaryString("RemoteActorRef:stop")
|
||||
val Stop = "RemoteActorRef:stop".intern
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1357,26 +1350,31 @@ object RemoteActorSystemMessage {
|
|||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] case class RemoteActorRef private[akka] (
|
||||
uuuid: String, val className: String, val hostname: String, val port: Int, _timeout: Long, loader: Option[ClassLoader])
|
||||
// uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef {
|
||||
uuuid: String,
|
||||
val className: String,
|
||||
val hostname: String,
|
||||
val port: Int,
|
||||
_timeout: Long,
|
||||
loader: Option[ClassLoader])
|
||||
extends ActorRef with ScalaActorRef {
|
||||
|
||||
RemoteClientModule.ensureRemotingEnabled
|
||||
|
||||
_uuid = uuuid
|
||||
timeout = _timeout
|
||||
|
||||
start
|
||||
lazy val remoteClient = RemoteClient.clientFor(hostname, port, loader)
|
||||
lazy val remoteClient = RemoteClientModule.clientFor(hostname, port, loader)
|
||||
|
||||
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
|
||||
remoteClient.send[Any](createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
|
||||
}
|
||||
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit =
|
||||
RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
|
||||
|
||||
def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||
message: Any,
|
||||
timeout: Long,
|
||||
senderOption: Option[ActorRef],
|
||||
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
|
||||
val future = remoteClient.send(createRemoteRequestProtocolBuilder(this, message, false, senderOption).build, senderFuture)
|
||||
val future = RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
|
||||
if (future.isDefined) future.get
|
||||
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
|
||||
}
|
||||
|
|
@ -1582,15 +1580,12 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef =>
|
|||
def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = {
|
||||
if (isRunning) {
|
||||
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
|
||||
val isTypedActor = message.isInstanceOf[JoinPoint]
|
||||
if (isTypedActor && TypedActor.isOneWay(message.asInstanceOf[JoinPoint])) {
|
||||
future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None)
|
||||
}
|
||||
val isMessageJoinPoint = TypedActorModule.resolveFutureIfMessageIsJoinPoint(message, future)
|
||||
try {
|
||||
future.await
|
||||
} catch {
|
||||
case e: FutureTimeoutException =>
|
||||
if (isTypedActor) throw e
|
||||
if (isMessageJoinPoint) throw e
|
||||
else None
|
||||
}
|
||||
if (future.exception.isDefined) throw future.exception.get
|
||||
|
|
@ -7,7 +7,7 @@ package se.scalablesolutions.akka.actor
|
|||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
import se.scalablesolutions.akka.remote.RemoteServer
|
||||
import se.scalablesolutions.akka.remote.RemoteServerModule
|
||||
import se.scalablesolutions.akka.AkkaException
|
||||
import Actor._
|
||||
|
||||
|
|
@ -162,8 +162,10 @@ sealed class Supervisor private[akka] (
|
|||
_childActors.put(className, actorRef :: currentActors)
|
||||
actorRef.lifeCycle = Some(lifeCycle)
|
||||
supervisor.link(actorRef)
|
||||
remoteAddress.foreach(address => RemoteServer.registerActor(
|
||||
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef))
|
||||
remoteAddress.foreach { address =>
|
||||
RemoteServerModule.registerActor(
|
||||
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef)
|
||||
}
|
||||
case supervisorConfig @ SupervisorConfig(_, _) => // recursive supervisor configuration
|
||||
val childSupervisor = Supervisor(supervisorConfig)
|
||||
supervisor.link(childSupervisor.supervisor)
|
||||
|
|
@ -4,19 +4,27 @@
|
|||
|
||||
package se.scalablesolutions.akka.config
|
||||
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
import se.scalablesolutions.akka.AkkaException
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
import se.scalablesolutions.akka.actor.{ActorRef, IllegalActorStateException}
|
||||
import se.scalablesolutions.akka.dispatch.CompletableFuture
|
||||
|
||||
import net.lag.configgy.{Config => CConfig, Configgy, ParseException}
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class ConfigurationException(message: String) extends AkkaException(message)
|
||||
class ModuleNotAvailableException(message: String) extends AkkaException(message)
|
||||
|
||||
object ConfigLogger extends Logging
|
||||
|
||||
/**
|
||||
* Loads up the configuration (from the akka.conf file).
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Config extends Logging {
|
||||
object Config {
|
||||
val VERSION = "1.0-SNAPSHOT"
|
||||
|
||||
// Set Multiverse options for max speed
|
||||
|
|
@ -37,7 +45,7 @@ object Config extends Logging {
|
|||
val configFile = System.getProperty("akka.config", "")
|
||||
try {
|
||||
Configgy.configure(configFile)
|
||||
log.info("Config loaded from -Dakka.config=%s", configFile)
|
||||
ConfigLogger.log.info("Config loaded from -Dakka.config=%s", configFile)
|
||||
} catch {
|
||||
case e: ParseException => throw new ConfigurationException(
|
||||
"Config could not be loaded from -Dakka.config=" + configFile +
|
||||
|
|
@ -47,7 +55,7 @@ object Config extends Logging {
|
|||
} else if (getClass.getClassLoader.getResource("akka.conf") != null) {
|
||||
try {
|
||||
Configgy.configureFromResource("akka.conf", getClass.getClassLoader)
|
||||
log.info("Config loaded from the application classpath.")
|
||||
ConfigLogger.log.info("Config loaded from the application classpath.")
|
||||
} catch {
|
||||
case e: ParseException => throw new ConfigurationException(
|
||||
"Can't load 'akka.conf' config file from application classpath," +
|
||||
|
|
@ -58,7 +66,7 @@ object Config extends Logging {
|
|||
try {
|
||||
val configFile = HOME.get + "/config/akka.conf"
|
||||
Configgy.configure(configFile)
|
||||
log.info("AKKA_HOME is defined as [%s], config loaded from [%s].", HOME.get, configFile)
|
||||
ConfigLogger.log.info("AKKA_HOME is defined as [%s], config loaded from [%s].", HOME.get, configFile)
|
||||
} catch {
|
||||
case e: ParseException => throw new ConfigurationException(
|
||||
"AKKA_HOME is defined as [" + HOME.get + "] " +
|
||||
|
|
@ -67,7 +75,7 @@ object Config extends Logging {
|
|||
}
|
||||
Configgy.config
|
||||
} else {
|
||||
log.warning(
|
||||
ConfigLogger.log.warning(
|
||||
"\nCan't load 'akka.conf'." +
|
||||
"\nOne of the three ways of locating the 'akka.conf' file needs to be defined:" +
|
||||
"\n\t1. Define the '-Dakka.config=...' system property option." +
|
||||
|
|
@ -106,7 +106,8 @@ object TransactionContainer extends Logging {
|
|||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class TransactionContainer private (val tm: Either[Option[UserTransaction], Option[TransactionManager]]) {
|
||||
class TransactionContainer private (
|
||||
val tm: Either[Option[UserTransaction], Option[TransactionManager]]) extends Logging {
|
||||
|
||||
def registerSynchronization(sync: Synchronization) = {
|
||||
TransactionContainer.findSynchronizationRegistry match { // try to use SynchronizationRegistry in JNDI
|
||||
|
|
@ -9,6 +9,8 @@ import se.scalablesolutions.akka.util.Logging
|
|||
import org.multiverse.api.{Transaction => MultiverseTransaction}
|
||||
import org.multiverse.templates.TransactionalCallable
|
||||
|
||||
object GlobalStm extends Logging
|
||||
|
||||
/**
|
||||
* Global transaction management, global in the context of multiple threads.
|
||||
* Use this if you need to have one transaction span multiple threads (or Actors).
|
||||
|
|
@ -23,12 +25,14 @@ import org.multiverse.templates.TransactionalCallable
|
|||
* }
|
||||
* </pre>
|
||||
*/
|
||||
class GlobalStm extends TransactionManagement with Logging {
|
||||
class GlobalStm extends TransactionManagement {
|
||||
|
||||
val DefaultGlobalTransactionConfig = TransactionConfig()
|
||||
val DefaultGlobalTransactionFactory = TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
|
||||
val DefaultGlobalTransactionFactory = TransactionFactory(
|
||||
DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
|
||||
|
||||
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T = atomic(factory)(body)
|
||||
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T =
|
||||
atomic(factory)(body)
|
||||
|
||||
def atomic[T](factory: TransactionFactory)(body: => T): T = {
|
||||
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
||||
|
|
@ -37,7 +41,8 @@ class GlobalStm extends TransactionManagement with Logging {
|
|||
factory.addHooks
|
||||
val result = body
|
||||
val txSet = getTransactionSetInScope
|
||||
log.trace("Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]")
|
||||
GlobalStm.log.trace(
|
||||
"Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]")
|
||||
try {
|
||||
txSet.tryJoinCommit(
|
||||
mtx,
|
||||
|
|
@ -9,6 +9,8 @@ import se.scalablesolutions.akka.util.Logging
|
|||
import org.multiverse.api.{Transaction => MultiverseTransaction}
|
||||
import org.multiverse.templates.TransactionalCallable
|
||||
|
||||
object LocalStm extends Logging
|
||||
|
||||
/**
|
||||
* Local transaction management, local in the context of threads.
|
||||
* Use this if you do <b>not</b> need to have one transaction span
|
||||
|
|
@ -27,16 +29,18 @@ import org.multiverse.templates.TransactionalCallable
|
|||
class LocalStm extends TransactionManagement with Logging {
|
||||
|
||||
val DefaultLocalTransactionConfig = TransactionConfig()
|
||||
val DefaultLocalTransactionFactory = TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction")
|
||||
val DefaultLocalTransactionFactory = TransactionFactory(
|
||||
DefaultLocalTransactionConfig, "DefaultLocalTransaction")
|
||||
|
||||
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T = atomic(factory)(body)
|
||||
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T =
|
||||
atomic(factory)(body)
|
||||
|
||||
def atomic[T](factory: TransactionFactory)(body: => T): T = {
|
||||
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
||||
def call(mtx: MultiverseTransaction): T = {
|
||||
factory.addHooks
|
||||
val result = body
|
||||
log.trace("Committing local transaction [" + mtx + "]")
|
||||
LocalStm.log.trace("Committing local transaction [" + mtx + "]")
|
||||
result
|
||||
}
|
||||
})
|
||||
21
akka-actors/src/test/resources/logback-test.xml
Normal file
21
akka-actors/src/test/resources/logback-test.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- For assistance related to logback-translator or configuration -->
|
||||
<!-- files in general, please contact the logback user mailing list -->
|
||||
<!-- at http://www.qos.ch/mailman/listinfo/logback-user -->
|
||||
<!-- -->
|
||||
<!-- For professional support please see -->
|
||||
<!-- http://www.qos.ch/shop/products/professionalSupport -->
|
||||
<!-- -->
|
||||
<configuration scan="false" debug="false">
|
||||
<!-- FOR AKKA INTERNAL USE ONLY -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>[%4p] [%d{ISO8601}] [%t] %c{1}: %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="se.scalablesolutions" level="DEBUG"/>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="stdout"/>
|
||||
</root>
|
||||
</configuration>
|
||||
44
akka-actors/src/test/scala/Messages.scala
Normal file
44
akka-actors/src/test/scala/Messages.scala
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka
|
||||
|
||||
import se.scalablesolutions.akka.serialization.Serializable
|
||||
import sbinary._
|
||||
import sbinary.Operations._
|
||||
|
||||
sealed abstract class TestMessage
|
||||
|
||||
case object Ping extends TestMessage
|
||||
case object Pong extends TestMessage
|
||||
case object OneWay extends TestMessage
|
||||
case object Die extends TestMessage
|
||||
case object NotifySupervisorExit extends TestMessage
|
||||
|
||||
case class User(val usernamePassword: Tuple2[String, String],
|
||||
val email: String,
|
||||
val age: Int)
|
||||
extends Serializable.SBinary[User] {
|
||||
def this() = this(null, null, 0)
|
||||
import sbinary.DefaultProtocol._
|
||||
implicit object UserFormat extends Format[User] {
|
||||
def reads(in : Input) = User(
|
||||
read[Tuple2[String, String]](in),
|
||||
read[String](in),
|
||||
read[Int](in))
|
||||
def writes(out: Output, value: User) = {
|
||||
write[Tuple2[String, String]](out, value.usernamePassword)
|
||||
write[String](out, value.email)
|
||||
write[Int](out, value.age)
|
||||
}
|
||||
}
|
||||
def fromBytes(bytes: Array[Byte]) = fromByteArray[User](bytes)
|
||||
def toBytes: Array[Byte] = toByteArray(this)
|
||||
}
|
||||
|
||||
case object RemotePing extends TestMessage
|
||||
case object RemotePong extends TestMessage
|
||||
case object RemoteOneWay extends TestMessage
|
||||
case object RemoteDie extends TestMessage
|
||||
case object RemoteNotifySupervisorExit extends TestMessage
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CyclicBarrier, TimeoutException}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
import Actor._
|
||||
|
||||
object ActorFireForgetRequestReplySpec {
|
||||
class ReplyActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self)
|
||||
|
||||
def receive = {
|
||||
case "Send" =>
|
||||
self.reply("Reply")
|
||||
case "SendImplicit" =>
|
||||
self.sender.get ! "ReplyImplicit"
|
||||
}
|
||||
}
|
||||
|
||||
class CrashingTemporaryActor extends Actor {
|
||||
self.lifeCycle = Some(LifeCycle(Temporary))
|
||||
|
||||
def receive = {
|
||||
case "Die" =>
|
||||
state.finished.await
|
||||
throw new Exception("Expected exception")
|
||||
}
|
||||
}
|
||||
|
||||
class SenderActor(replyActor: ActorRef) extends Actor {
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self)
|
||||
|
||||
def receive = {
|
||||
case "Init" => replyActor ! "Send"
|
||||
case "Reply" => {
|
||||
state.s = "Reply"
|
||||
state.finished.await
|
||||
}
|
||||
case "InitImplicit" => replyActor ! "SendImplicit"
|
||||
case "ReplyImplicit" => {
|
||||
state.s = "ReplyImplicit"
|
||||
state.finished.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object state {
|
||||
var s = "NIL"
|
||||
val finished = new CyclicBarrier(2)
|
||||
}
|
||||
}
|
||||
|
||||
class ActorFireForgetRequestReplySpec extends JUnitSuite {
|
||||
import ActorFireForgetRequestReplySpec._
|
||||
|
||||
@Test
|
||||
def shouldReplyToBangMessageUsingReply = {
|
||||
state.finished.reset
|
||||
val replyActor = actorOf[ReplyActor].start
|
||||
val senderActor = actorOf(new SenderActor(replyActor)).start
|
||||
senderActor ! "Init"
|
||||
try { state.finished.await(1L, TimeUnit.SECONDS) }
|
||||
catch { case e: TimeoutException => fail("Never got the message") }
|
||||
assert("Reply" === state.s)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldReplyToBangMessageUsingImplicitSender = {
|
||||
state.finished.reset
|
||||
val replyActor = actorOf[ReplyActor].start
|
||||
val senderActor = actorOf(new SenderActor(replyActor)).start
|
||||
senderActor ! "InitImplicit"
|
||||
try { state.finished.await(1L, TimeUnit.SECONDS) }
|
||||
catch { case e: TimeoutException => fail("Never got the message") }
|
||||
assert("ReplyImplicit" === state.s)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldShutdownCrashedTemporaryActor = {
|
||||
state.finished.reset
|
||||
val actor = actorOf[CrashingTemporaryActor].start
|
||||
assert(actor.isRunning)
|
||||
actor ! "Die"
|
||||
try { state.finished.await(1L, TimeUnit.SECONDS) }
|
||||
catch { case e: TimeoutException => fail("Never got the message") }
|
||||
Thread.sleep(100)
|
||||
assert(actor.isShutdown)
|
||||
}
|
||||
}
|
||||
111
akka-actors/src/test/scala/actor/actor/AgentSpec.scala
Normal file
111
akka-actors/src/test/scala/actor/actor/AgentSpec.scala
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor.transactor
|
||||
import org.scalatest.Suite
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.Test
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class AgentSpec extends junit.framework.TestCase with Suite with MustMatchers {
|
||||
|
||||
@Test def testSendFun = {
|
||||
val agent = Agent(5)
|
||||
agent send (_ + 1)
|
||||
agent send (_ * 2)
|
||||
val result = agent()
|
||||
result must be(12)
|
||||
agent.close
|
||||
}
|
||||
|
||||
@Test def testSendValue = {
|
||||
val agent = Agent(5)
|
||||
agent send 6
|
||||
val result = agent()
|
||||
result must be(6)
|
||||
agent.close
|
||||
}
|
||||
|
||||
@Test def testSendProc = {
|
||||
val agent = Agent(5)
|
||||
var result = 0
|
||||
val latch = new CountDownLatch(2)
|
||||
agent sendProc { e => result += e; latch.countDown }
|
||||
agent sendProc { e => result += e; latch.countDown }
|
||||
assert(latch.await(5, TimeUnit.SECONDS))
|
||||
result must be(10)
|
||||
agent.close
|
||||
}
|
||||
|
||||
@Test def testOneAgentsendWithinEnlosingTransactionSuccess = {
|
||||
case object Go
|
||||
val agent = Agent(5)
|
||||
val latch = new CountDownLatch(1)
|
||||
val tx = transactor {
|
||||
case Go => agent send { e => latch.countDown; e + 1 }
|
||||
}
|
||||
tx ! Go
|
||||
assert(latch.await(5, TimeUnit.SECONDS))
|
||||
val result = agent()
|
||||
result must be(6)
|
||||
agent.close
|
||||
tx.stop
|
||||
}
|
||||
|
||||
/*
|
||||
// Strange test - do we really need it?
|
||||
@Test def testDoingAgentGetInEnlosingTransactionShouldYieldException = {
|
||||
case object Go
|
||||
val latch = new CountDownLatch(1)
|
||||
val agent = Agent(5)
|
||||
val tx = transactor {
|
||||
case Go =>
|
||||
agent send (_ * 2)
|
||||
try { agent() }
|
||||
catch {
|
||||
case _ => latch.countDown
|
||||
}
|
||||
}
|
||||
tx ! Go
|
||||
assert(latch.await(5, TimeUnit.SECONDS))
|
||||
agent.close
|
||||
tx.stop
|
||||
assert(true)
|
||||
}
|
||||
*/
|
||||
|
||||
@Test def testAgentForeach = {
|
||||
val agent1 = Agent(3)
|
||||
var result = 0
|
||||
for (first <- agent1) {
|
||||
result = first + 1
|
||||
}
|
||||
result must be(4)
|
||||
agent1.close
|
||||
}
|
||||
|
||||
@Test def testAgentMap = {
|
||||
val agent1 = Agent(3)
|
||||
val result = for (first <- agent1) yield first + 1
|
||||
result() must be(4)
|
||||
result.close
|
||||
agent1.close
|
||||
}
|
||||
|
||||
@Test def testAgentFlatMap = {
|
||||
val agent1 = Agent(3)
|
||||
val agent2 = Agent(5)
|
||||
val result = for {
|
||||
first <- agent1
|
||||
second <- agent2
|
||||
} yield second + first
|
||||
result() must be(8)
|
||||
result.close
|
||||
agent1.close
|
||||
agent2.close
|
||||
}
|
||||
}
|
||||
119
akka-actors/src/test/scala/actor/actor/Bench.scala
Normal file
119
akka-actors/src/test/scala/actor/actor/Bench.scala
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* The Computer Language Benchmarks Game
|
||||
http://shootout.alioth.debian.org/
|
||||
contributed by Julien Gaugaz
|
||||
inspired by the version contributed by Yura Taras and modified by Isaac Gouy
|
||||
*/
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
|
||||
object Chameneos {
|
||||
|
||||
sealed trait ChameneosEvent
|
||||
case class Meet(from: ActorRef, colour: Colour) extends ChameneosEvent
|
||||
case class Change(colour: Colour) extends ChameneosEvent
|
||||
case class MeetingCount(count: Int) extends ChameneosEvent
|
||||
case object Exit extends ChameneosEvent
|
||||
|
||||
abstract class Colour
|
||||
case object RED extends Colour
|
||||
case object YELLOW extends Colour
|
||||
case object BLUE extends Colour
|
||||
case object FADED extends Colour
|
||||
|
||||
val colours = Array[Colour](BLUE, RED, YELLOW)
|
||||
|
||||
var start = 0L
|
||||
var end = 0L
|
||||
|
||||
class Chameneo(var mall: ActorRef, var colour: Colour, cid: Int) extends Actor {
|
||||
var meetings = 0
|
||||
self.start
|
||||
mall ! Meet(self, colour)
|
||||
|
||||
def receive = {
|
||||
case Meet(from, otherColour) =>
|
||||
colour = complement(otherColour)
|
||||
meetings = meetings +1
|
||||
from ! Change(colour)
|
||||
mall ! Meet(self, colour)
|
||||
|
||||
case Change(newColour) =>
|
||||
colour = newColour
|
||||
meetings = meetings +1
|
||||
mall ! Meet(self, colour)
|
||||
|
||||
case Exit =>
|
||||
colour = FADED
|
||||
self.sender.get ! MeetingCount(meetings)
|
||||
}
|
||||
|
||||
def complement(otherColour: Colour): Colour = colour match {
|
||||
case RED => otherColour match {
|
||||
case RED => RED
|
||||
case YELLOW => BLUE
|
||||
case BLUE => YELLOW
|
||||
case FADED => FADED
|
||||
}
|
||||
case YELLOW => otherColour match {
|
||||
case RED => BLUE
|
||||
case YELLOW => YELLOW
|
||||
case BLUE => RED
|
||||
case FADED => FADED
|
||||
}
|
||||
case BLUE => otherColour match {
|
||||
case RED => YELLOW
|
||||
case YELLOW => RED
|
||||
case BLUE => BLUE
|
||||
case FADED => FADED
|
||||
}
|
||||
case FADED => FADED
|
||||
}
|
||||
|
||||
override def toString = cid + "(" + colour + ")"
|
||||
}
|
||||
|
||||
class Mall(var n: Int, numChameneos: Int) extends Actor {
|
||||
var waitingChameneo: Option[ActorRef] = None
|
||||
var sumMeetings = 0
|
||||
var numFaded = 0
|
||||
|
||||
override def init = {
|
||||
for (i <- 0 until numChameneos) actorOf(new Chameneo(self, colours(i % 3), i))
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case MeetingCount(i) =>
|
||||
numFaded += 1
|
||||
sumMeetings += i
|
||||
if (numFaded == numChameneos) {
|
||||
Chameneos.end = System.currentTimeMillis
|
||||
self.stop
|
||||
}
|
||||
|
||||
case msg @ Meet(a, c) =>
|
||||
if (n > 0) {
|
||||
waitingChameneo match {
|
||||
case Some(chameneo) =>
|
||||
n -= 1
|
||||
chameneo ! msg
|
||||
waitingChameneo = None
|
||||
case None => waitingChameneo = self.sender
|
||||
}
|
||||
} else {
|
||||
waitingChameneo.foreach(_ ! Exit)
|
||||
self.sender.get ! Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def run {
|
||||
// System.setProperty("akka.config", "akka.conf")
|
||||
Chameneos.start = System.currentTimeMillis
|
||||
actorOf(new Mall(1000000, 4)).start
|
||||
Thread.sleep(10000)
|
||||
println("Elapsed: " + (end - start))
|
||||
}
|
||||
|
||||
def main(args : Array[String]): Unit = run
|
||||
}
|
||||
82
akka-actors/src/test/scala/actor/actor/FSMActorSpec.scala
Normal file
82
akka-actors/src/test/scala/actor/actor/FSMActorSpec.scala
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object FSMActorSpec {
|
||||
|
||||
class Lock(code: String,
|
||||
timeout: Int,
|
||||
unlockedLatch: StandardLatch,
|
||||
lockedLatch: StandardLatch) extends Actor with FSM[CodeState] {
|
||||
|
||||
def initialState = State(NextState, locked, CodeState("", code))
|
||||
|
||||
def locked: StateFunction = {
|
||||
case Event(digit: Char, CodeState(soFar, code)) => {
|
||||
soFar + digit match {
|
||||
case incomplete if incomplete.length < code.length =>
|
||||
State(NextState, locked, CodeState(incomplete, code))
|
||||
case codeTry if (codeTry == code) => {
|
||||
doUnlock
|
||||
State(NextState, open, CodeState("", code), Some(timeout))
|
||||
}
|
||||
case wrong => {
|
||||
log.error("Wrong code %s", wrong)
|
||||
State(NextState, locked, CodeState("", code))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def open: StateFunction = {
|
||||
case Event(StateTimeout, stateData) => {
|
||||
doLock
|
||||
State(NextState, locked, stateData)
|
||||
}
|
||||
}
|
||||
|
||||
private def doLock() {
|
||||
log.info("Locked")
|
||||
lockedLatch.open
|
||||
}
|
||||
|
||||
private def doUnlock = {
|
||||
log.info("Unlocked")
|
||||
unlockedLatch.open
|
||||
}
|
||||
}
|
||||
|
||||
case class CodeState(soFar: String, code: String)
|
||||
}
|
||||
|
||||
class FSMActorSpec extends JUnitSuite {
|
||||
import FSMActorSpec._
|
||||
|
||||
@Test
|
||||
def unlockTheLock = {
|
||||
val unlockedLatch = new StandardLatch
|
||||
val lockedLatch = new StandardLatch
|
||||
|
||||
// lock that locked after being open for 1 sec
|
||||
val lock = Actor.actorOf(new Lock("33221", 1000, unlockedLatch, lockedLatch)).start
|
||||
|
||||
lock ! '3'
|
||||
lock ! '3'
|
||||
lock ! '2'
|
||||
lock ! '2'
|
||||
lock ! '1'
|
||||
|
||||
assert(unlockedLatch.tryAwait(1, TimeUnit.SECONDS))
|
||||
assert(lockedLatch.tryAwait(2, TimeUnit.SECONDS))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import Actor._
|
||||
|
||||
object ForwardActorSpec {
|
||||
object ForwardState {
|
||||
var sender: Option[ActorRef] = None
|
||||
}
|
||||
|
||||
class ReceiverActor extends Actor {
|
||||
val latch = new CountDownLatch(1)
|
||||
def receive = {
|
||||
case "SendBang" => {
|
||||
ForwardState.sender = self.sender
|
||||
latch.countDown
|
||||
}
|
||||
case "SendBangBang" => self.reply("SendBangBang")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ForwardActor extends Actor {
|
||||
val receiverActor = actorOf[ReceiverActor]
|
||||
receiverActor.start
|
||||
def receive = {
|
||||
case "SendBang" => receiverActor.forward("SendBang")
|
||||
case "SendBangBang" => receiverActor.forward("SendBangBang")
|
||||
}
|
||||
}
|
||||
|
||||
class BangSenderActor extends Actor {
|
||||
val forwardActor = actorOf[ForwardActor]
|
||||
forwardActor.start
|
||||
forwardActor ! "SendBang"
|
||||
def receive = {
|
||||
case _ => {}
|
||||
}
|
||||
}
|
||||
|
||||
class BangBangSenderActor extends Actor {
|
||||
val latch = new CountDownLatch(1)
|
||||
val forwardActor = actorOf[ForwardActor]
|
||||
forwardActor.start
|
||||
(forwardActor !! "SendBangBang") match {
|
||||
case Some(_) => latch.countDown
|
||||
case None => {}
|
||||
}
|
||||
def receive = {
|
||||
case _ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ForwardActorSpec extends JUnitSuite {
|
||||
import ForwardActorSpec._
|
||||
|
||||
@Test
|
||||
def shouldForwardActorReferenceWhenInvokingForwardOnBang {
|
||||
val senderActor = actorOf[BangSenderActor]
|
||||
val latch = senderActor.actor.asInstanceOf[BangSenderActor]
|
||||
.forwardActor.actor.asInstanceOf[ForwardActor]
|
||||
.receiverActor.actor.asInstanceOf[ReceiverActor]
|
||||
.latch
|
||||
senderActor.start
|
||||
assert(latch.await(1L, TimeUnit.SECONDS))
|
||||
assert(ForwardState.sender ne null)
|
||||
assert(senderActor.toString === ForwardState.sender.get.toString)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldForwardActorReferenceWhenInvokingForwardOnBangBang {
|
||||
val senderActor = actorOf[BangBangSenderActor]
|
||||
senderActor.start
|
||||
val latch = senderActor.actor.asInstanceOf[BangBangSenderActor].latch
|
||||
assert(latch.await(1L, TimeUnit.SECONDS))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import Actor._
|
||||
|
||||
class ReceiveTimeoutSpec extends JUnitSuite {
|
||||
|
||||
@Test def receiveShouldGetTimeout= {
|
||||
|
||||
val timeoutLatch = new StandardLatch
|
||||
|
||||
val timeoutActor = actorOf(new Actor {
|
||||
self.receiveTimeout = Some(500L)
|
||||
|
||||
protected def receive = {
|
||||
case ReceiveTimeout => timeoutLatch.open
|
||||
}
|
||||
}).start
|
||||
|
||||
assert(timeoutLatch.tryAwait(3, TimeUnit.SECONDS))
|
||||
}
|
||||
|
||||
@Test def swappedReceiveShouldAlsoGetTimout = {
|
||||
val timeoutLatch = new StandardLatch
|
||||
|
||||
val timeoutActor = actorOf(new Actor {
|
||||
self.receiveTimeout = Some(500L)
|
||||
|
||||
protected def receive = {
|
||||
case ReceiveTimeout => timeoutLatch.open
|
||||
}
|
||||
}).start
|
||||
|
||||
// after max 1 second the timeout should already been sent
|
||||
assert(timeoutLatch.tryAwait(3, TimeUnit.SECONDS))
|
||||
|
||||
val swappedLatch = new StandardLatch
|
||||
timeoutActor ! HotSwap(Some{
|
||||
case ReceiveTimeout => swappedLatch.open
|
||||
})
|
||||
|
||||
assert(swappedLatch.tryAwait(3, TimeUnit.SECONDS))
|
||||
}
|
||||
|
||||
@Test def timeoutShouldBeCancelledAfterRegularReceive = {
|
||||
|
||||
val timeoutLatch = new StandardLatch
|
||||
case object Tick
|
||||
val timeoutActor = actorOf(new Actor {
|
||||
self.receiveTimeout = Some(500L)
|
||||
|
||||
protected def receive = {
|
||||
case Tick => ()
|
||||
case ReceiveTimeout => timeoutLatch.open
|
||||
}
|
||||
}).start
|
||||
timeoutActor ! Tick
|
||||
|
||||
assert(timeoutLatch.tryAwait(2, TimeUnit.SECONDS) == false)
|
||||
}
|
||||
|
||||
@Test def timeoutShouldNotBeSentWhenNotSpecified = {
|
||||
val timeoutLatch = new StandardLatch
|
||||
val timeoutActor = actorOf(new Actor {
|
||||
|
||||
protected def receive = {
|
||||
case ReceiveTimeout => timeoutLatch.open
|
||||
}
|
||||
}).start
|
||||
|
||||
assert(timeoutLatch.tryAwait(1, TimeUnit.SECONDS) == false)
|
||||
}
|
||||
}
|
||||
255
akka-actors/src/test/scala/actor/actor/TransactorSpec.scala
Normal file
255
akka-actors/src/test/scala/actor/actor/TransactorSpec.scala
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.stm.{Ref, TransactionalMap, TransactionalVector}
|
||||
import Actor._
|
||||
|
||||
object TransactorSpec {
|
||||
case class GetMapState(key: String)
|
||||
case object GetVectorState
|
||||
case object GetVectorSize
|
||||
case object GetRefState
|
||||
|
||||
case class SetMapState(key: String, value: String)
|
||||
case class SetVectorState(key: String)
|
||||
case class SetRefState(key: String)
|
||||
case class Success(key: String, value: String)
|
||||
case class Failure(key: String, value: String, failer: ActorRef)
|
||||
|
||||
case class SetMapStateOneWay(key: String, value: String)
|
||||
case class SetVectorStateOneWay(key: String)
|
||||
case class SetRefStateOneWay(key: String)
|
||||
case class SuccessOneWay(key: String, value: String)
|
||||
case class FailureOneWay(key: String, value: String, failer: ActorRef)
|
||||
|
||||
case object GetNotifier
|
||||
}
|
||||
import TransactorSpec._
|
||||
|
||||
class StatefulTransactor(expectedInvocationCount: Int) extends Transactor {
|
||||
def this() = this(0)
|
||||
self.timeout = 5000
|
||||
|
||||
val notifier = new CountDownLatch(expectedInvocationCount)
|
||||
|
||||
private val mapState = TransactionalMap[String, String]()
|
||||
private val vectorState = TransactionalVector[String]()
|
||||
private val refState = Ref[String]()
|
||||
|
||||
def receive = {
|
||||
case GetNotifier =>
|
||||
self.reply(notifier)
|
||||
case GetMapState(key) =>
|
||||
self.reply(mapState.get(key).get)
|
||||
notifier.countDown
|
||||
case GetVectorSize =>
|
||||
self.reply(vectorState.length.asInstanceOf[AnyRef])
|
||||
notifier.countDown
|
||||
case GetRefState =>
|
||||
self.reply(refState.get)
|
||||
notifier.countDown
|
||||
case SetMapState(key, msg) =>
|
||||
mapState.put(key, msg)
|
||||
self.reply(msg)
|
||||
notifier.countDown
|
||||
case SetVectorState(msg) =>
|
||||
vectorState.add(msg)
|
||||
self.reply(msg)
|
||||
notifier.countDown
|
||||
case SetRefState(msg) =>
|
||||
refState.swap(msg)
|
||||
self.reply(msg)
|
||||
notifier.countDown
|
||||
case Success(key, msg) =>
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
self.reply(msg)
|
||||
notifier.countDown
|
||||
case Failure(key, msg, failer) =>
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
failer !! "Failure"
|
||||
self.reply(msg)
|
||||
notifier.countDown
|
||||
case SetMapStateOneWay(key, msg) =>
|
||||
mapState.put(key, msg)
|
||||
notifier.countDown
|
||||
case SetVectorStateOneWay(msg) =>
|
||||
vectorState.add(msg)
|
||||
notifier.countDown
|
||||
case SetRefStateOneWay(msg) =>
|
||||
refState.swap(msg)
|
||||
notifier.countDown
|
||||
case SuccessOneWay(key, msg) =>
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
notifier.countDown
|
||||
case FailureOneWay(key, msg, failer) =>
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
notifier.countDown
|
||||
failer ! "Failure"
|
||||
}
|
||||
}
|
||||
|
||||
@serializable
|
||||
class FailerTransactor extends Transactor {
|
||||
|
||||
def receive = {
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
|
||||
class TransactorSpec extends JUnitSuite {
|
||||
|
||||
@Test
|
||||
def shouldOneWayMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
stateful ! SetMapStateOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state
|
||||
stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(1, TimeUnit.SECONDS))
|
||||
assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state
|
||||
stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldOneWayMapShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
stateful ! SetMapStateOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(5, TimeUnit.SECONDS))
|
||||
assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldMapShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
try {
|
||||
stateful !! Failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
} catch {case e: RuntimeException => {}}
|
||||
assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldOneWayVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
stateful ! SetVectorStateOneWay("init") // set init state
|
||||
stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(1, TimeUnit.SECONDS))
|
||||
assert(2 === (stateful !! GetVectorSize).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetVectorState("init") // set init state
|
||||
stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
assert(2 === (stateful !! GetVectorSize).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldOneWayVectorShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
stateful ! SetVectorStateOneWay("init") // set init state
|
||||
Thread.sleep(1000)
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(1, TimeUnit.SECONDS))
|
||||
assert(1 === (stateful !! GetVectorSize).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldVectorShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetVectorState("init") // set init state
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
try {
|
||||
stateful !! Failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
} catch {case e: RuntimeException => {}}
|
||||
assert(1 === (stateful !! GetVectorSize).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldOneWayRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
stateful ! SetRefStateOneWay("init") // set init state
|
||||
stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(1, TimeUnit.SECONDS))
|
||||
assert("new state" === (stateful !! GetRefState).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetRefState("init") // set init state
|
||||
stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
assert("new state" === (stateful !! GetRefState).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldOneWayRefShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf(new StatefulTransactor(2))
|
||||
stateful.start
|
||||
stateful ! SetRefStateOneWay("init") // set init state
|
||||
Thread.sleep(1000)
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
val notifier = (stateful !! GetNotifier).as[CountDownLatch]
|
||||
assert(notifier.get.await(1, TimeUnit.SECONDS))
|
||||
assert("init" === (stateful !! (GetRefState, 1000000)).get) // check that state is == init state
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldRefShouldRollbackStateForStatefulServerInCaseOfFailure = {
|
||||
val stateful = actorOf[StatefulTransactor]
|
||||
stateful.start
|
||||
stateful !! SetRefState("init") // set init state
|
||||
val failer = actorOf[FailerTransactor]
|
||||
failer.start
|
||||
try {
|
||||
stateful !! Failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
} catch {case e: RuntimeException => {}}
|
||||
assert("init" === (stateful !! GetRefState).get) // check that state is == init state
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import Actor._
|
||||
import se.scalablesolutions.akka.config.OneForOneStrategy
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig.{Permanent, LifeCycle}
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
|
||||
class RestartStrategySpec extends JUnitSuite {
|
||||
|
||||
object Ping
|
||||
object Crash
|
||||
|
||||
@Test
|
||||
def slaveShouldStayDeadAfterMaxRestarts = {
|
||||
|
||||
val boss = actorOf(new Actor{
|
||||
self.trapExit = List(classOf[Throwable])
|
||||
self.faultHandler = Some(OneForOneStrategy(1, 1000))
|
||||
protected def receive = { case _ => () }
|
||||
}).start
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val secondRestartLatch = new StandardLatch
|
||||
val countDownLatch = new CountDownLatch(2)
|
||||
|
||||
|
||||
val slave = actorOf(new Actor{
|
||||
|
||||
protected def receive = {
|
||||
case Ping => countDownLatch.countDown
|
||||
case Crash => throw new Exception("Crashing...")
|
||||
}
|
||||
override def postRestart(reason: Throwable) = {
|
||||
restartLatch.open
|
||||
}
|
||||
|
||||
override def shutdown = {
|
||||
if (restartLatch.isOpen) {
|
||||
secondRestartLatch.open
|
||||
}
|
||||
}
|
||||
})
|
||||
boss.startLink(slave)
|
||||
|
||||
slave ! Ping
|
||||
slave ! Crash
|
||||
slave ! Ping
|
||||
|
||||
// test restart and post restart ping
|
||||
assert(restartLatch.tryAwait(1, TimeUnit.SECONDS))
|
||||
assert(countDownLatch.await(1, TimeUnit.SECONDS))
|
||||
|
||||
// now crash again... should not restart
|
||||
slave ! Crash
|
||||
|
||||
assert(secondRestartLatch.tryAwait(1, TimeUnit.SECONDS))
|
||||
val exceptionLatch = new StandardLatch
|
||||
try {
|
||||
slave ! Ping // this should fail
|
||||
} catch {
|
||||
case e => exceptionLatch.open // expected here
|
||||
}
|
||||
assert(exceptionLatch.tryAwait(1, TimeUnit.SECONDS))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import Actor._
|
||||
import se.scalablesolutions.akka.config.OneForOneStrategy
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
|
||||
object SupervisorHierarchySpec {
|
||||
class FireWorkerException(msg: String) extends Exception(msg)
|
||||
|
||||
class CountDownActor(countDown: CountDownLatch) extends Actor {
|
||||
protected def receive = { case _ => () }
|
||||
override def postRestart(reason: Throwable) = countDown.countDown
|
||||
}
|
||||
|
||||
class CrasherActor extends Actor {
|
||||
protected def receive = { case _ => () }
|
||||
}
|
||||
}
|
||||
|
||||
class SupervisorHierarchySpec extends JUnitSuite {
|
||||
import SupervisorHierarchySpec._
|
||||
|
||||
@Test
|
||||
def killWorkerShouldRestartMangerAndOtherWorkers = {
|
||||
val countDown = new CountDownLatch(4)
|
||||
|
||||
val workerOne = actorOf(new CountDownActor(countDown))
|
||||
val workerTwo = actorOf(new CountDownActor(countDown))
|
||||
val workerThree = actorOf(new CountDownActor(countDown))
|
||||
|
||||
val boss = actorOf(new Actor{
|
||||
self.trapExit = List(classOf[Throwable])
|
||||
self.faultHandler = Some(OneForOneStrategy(5, 1000))
|
||||
|
||||
protected def receive = { case _ => () }
|
||||
}).start
|
||||
|
||||
val manager = actorOf(new CountDownActor(countDown))
|
||||
boss.startLink(manager)
|
||||
|
||||
manager.startLink(workerOne)
|
||||
manager.startLink(workerTwo)
|
||||
manager.startLink(workerThree)
|
||||
|
||||
workerOne ! Exit(workerOne, new FireWorkerException("Fire the worker!"))
|
||||
|
||||
// manager + all workers should be restarted by only killing a worker
|
||||
// manager doesn't trap exits, so boss will restart manager
|
||||
|
||||
assert(countDown.await(2, TimeUnit.SECONDS))
|
||||
}
|
||||
|
||||
@Test
|
||||
def supervisorShouldReceiveNotificationMessageWhenMaximumNumberOfRestartsWithinTimeRangeIsReached = {
|
||||
val countDown = new CountDownLatch(2)
|
||||
val crasher = actorOf(new CountDownActor(countDown))
|
||||
val boss = actorOf(new Actor{
|
||||
self.trapExit = List(classOf[Throwable])
|
||||
self.faultHandler = Some(OneForOneStrategy(1, 5000))
|
||||
protected def receive = {
|
||||
case MaximumNumberOfRestartsWithinTimeRangeReached(_, _, _, _) =>
|
||||
countDown.countDown
|
||||
}
|
||||
}).start
|
||||
boss.startLink(crasher)
|
||||
|
||||
crasher ! Exit(crasher, new FireWorkerException("Fire the worker!"))
|
||||
crasher ! Exit(crasher, new FireWorkerException("Fire the worker!"))
|
||||
|
||||
assert(countDown.await(2, TimeUnit.SECONDS))
|
||||
}
|
||||
}
|
||||
|
||||
605
akka-actors/src/test/scala/actor/supervisor/SupervisorSpec.scala
Normal file
605
akka-actors/src/test/scala/actor/supervisor/SupervisorSpec.scala
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.config.OneForOneStrategy
|
||||
import se.scalablesolutions.akka.{OneWay, Die, Ping}
|
||||
import Actor._
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.{TimeUnit, LinkedBlockingQueue}
|
||||
|
||||
object SupervisorSpec {
|
||||
var messageLog = new LinkedBlockingQueue[String]
|
||||
var oneWayLog = new LinkedBlockingQueue[String]
|
||||
|
||||
def clearMessageLogs {
|
||||
messageLog.clear
|
||||
oneWayLog.clear
|
||||
}
|
||||
|
||||
class PingPong1Actor extends Actor {
|
||||
import self._
|
||||
//dispatcher = Dispatchers.newThreadBasedDispatcher(self)
|
||||
def receive = {
|
||||
case Ping =>
|
||||
messageLog.put("ping")
|
||||
reply("pong")
|
||||
|
||||
case OneWay =>
|
||||
oneWayLog.put("oneway")
|
||||
|
||||
case Die =>
|
||||
println("******************** GOT DIE 1")
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
override def postRestart(reason: Throwable) {
|
||||
println("******************** restart 1")
|
||||
messageLog.put(reason.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
class PingPong2Actor extends Actor {
|
||||
import self._
|
||||
def receive = {
|
||||
case Ping =>
|
||||
messageLog.put("ping")
|
||||
reply("pong")
|
||||
case Die =>
|
||||
println("******************** GOT DIE 2")
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
override def postRestart(reason: Throwable) {
|
||||
println("******************** restart 2")
|
||||
messageLog.put(reason.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
class PingPong3Actor extends Actor {
|
||||
import self._
|
||||
def receive = {
|
||||
case Ping =>
|
||||
messageLog.put("ping")
|
||||
reply("pong")
|
||||
case Die =>
|
||||
println("******************** GOT DIE 3")
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
|
||||
override def postRestart(reason: Throwable) {
|
||||
println("******************** restart 3")
|
||||
messageLog.put(reason.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
class TemporaryActor extends Actor {
|
||||
import self._
|
||||
lifeCycle = Some(LifeCycle(Temporary))
|
||||
def receive = {
|
||||
case Ping =>
|
||||
messageLog.put("ping")
|
||||
reply("pong")
|
||||
case Die =>
|
||||
println("******************** GOT DIE 3")
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
|
||||
override def postRestart(reason: Throwable) {
|
||||
println("******************** restart temporary")
|
||||
messageLog.put(reason.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
class Master extends Actor {
|
||||
self.trapExit = classOf[Exception] :: Nil
|
||||
self.faultHandler = Some(OneForOneStrategy(5, 1000))
|
||||
val temp = self.spawnLink[TemporaryActor]
|
||||
override def receive = {
|
||||
case Die => temp !! (Die, 5000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class SupervisorSpec extends JUnitSuite {
|
||||
import SupervisorSpec._
|
||||
|
||||
var pingpong1: ActorRef = _
|
||||
var pingpong2: ActorRef = _
|
||||
var pingpong3: ActorRef = _
|
||||
var temporaryActor: ActorRef = _
|
||||
|
||||
/*
|
||||
@Test def shouldStartServer = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
sup.start
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
*/
|
||||
@Test def shoulNotRestartProgrammaticallyLinkedTemporaryActor = {
|
||||
clearMessageLogs
|
||||
val master = actorOf[Master].start
|
||||
|
||||
intercept[RuntimeException] {
|
||||
master !! (Die, 5000)
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
assert(messageLog.size === 0)
|
||||
}
|
||||
|
||||
@Test def shoulNotRestartTemporaryActor = {
|
||||
clearMessageLogs
|
||||
val sup = getTemporaryActorAllForOneSupervisor
|
||||
|
||||
intercept[RuntimeException] {
|
||||
temporaryActor !! (Die, 5000)
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
assert(messageLog.size === 0)
|
||||
}
|
||||
|
||||
@Test def shouldStartServerForNestedSupervisorHierarchy = {
|
||||
clearMessageLogs
|
||||
val sup = getNestedSupervisorsAllForOneConf
|
||||
sup.start
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillSingleActorOneForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
|
||||
intercept[RuntimeException] {
|
||||
pingpong1 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldCallKillCallSingleActorOneForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
intercept[RuntimeException] {
|
||||
pingpong1 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillSingleActorAllForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
|
||||
intercept[RuntimeException] {
|
||||
pingpong1 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldCallKillCallSingleActorAllForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorAllForOneSupervisor
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
intercept[RuntimeException] {
|
||||
pingpong1 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillMultipleActorsOneForOne1 = {
|
||||
clearMessageLogs
|
||||
val sup = getMultipleActorsOneForOneConf
|
||||
|
||||
intercept[RuntimeException] {
|
||||
pingpong1 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillMultipleActorsOneForOne2 = {
|
||||
clearMessageLogs
|
||||
val sup = getMultipleActorsOneForOneConf
|
||||
|
||||
intercept[RuntimeException] {
|
||||
pingpong3 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillCallMultipleActorsOneForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getMultipleActorsOneForOneConf
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
intercept[RuntimeException] {
|
||||
pingpong2 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldKillMultipleActorsAllForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getMultipleActorsAllForOneConf
|
||||
|
||||
intercept[RuntimeException] {
|
||||
pingpong2 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldCallKillCallMultipleActorsAllForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getMultipleActorsAllForOneConf
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
intercept[RuntimeException] {
|
||||
pingpong2 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldOneWayKillSingleActorOneForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
|
||||
pingpong1 ! Die
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldOneWayCallKillCallSingleActorOneForOne = {
|
||||
clearMessageLogs
|
||||
val sup = getSingleActorOneForOneSupervisor
|
||||
|
||||
pingpong1 ! OneWay
|
||||
|
||||
expect("oneway") {
|
||||
oneWayLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
pingpong1 ! Die
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
pingpong1 ! OneWay
|
||||
|
||||
expect("oneway") {
|
||||
oneWayLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test def shouldRestartKilledActorsForNestedSupervisorHierarchy = {
|
||||
clearMessageLogs
|
||||
val sup = getNestedSupervisorsAllForOneConf
|
||||
|
||||
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
intercept[RuntimeException] {
|
||||
pingpong2 !! (Die, 5000)
|
||||
}
|
||||
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5 , TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("Expected exception; to test fault-tolerance") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("pong") {
|
||||
(pingpong1 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong2 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("pong") {
|
||||
(pingpong3 !! (Ping, 5000)).getOrElse("nil")
|
||||
}
|
||||
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
expect("ping") {
|
||||
messageLog.poll(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// Create some supervisors with different configurations
|
||||
|
||||
def getTemporaryActorAllForOneSupervisor: Supervisor = {
|
||||
temporaryActor = actorOf[TemporaryActor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
temporaryActor,
|
||||
LifeCycle(Temporary))
|
||||
:: Nil))
|
||||
}
|
||||
|
||||
def getSingleActorAllForOneSupervisor: Supervisor = {
|
||||
pingpong1 = actorOf[PingPong1Actor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil))
|
||||
}
|
||||
|
||||
def getSingleActorOneForOneSupervisor: Supervisor = {
|
||||
pingpong1 = actorOf[PingPong1Actor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(OneForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil))
|
||||
}
|
||||
|
||||
def getMultipleActorsAllForOneConf: Supervisor = {
|
||||
pingpong1 = actorOf[PingPong1Actor].start
|
||||
pingpong2 = actorOf[PingPong2Actor].start
|
||||
pingpong3 = actorOf[PingPong3Actor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
Supervise(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
Supervise(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil))
|
||||
}
|
||||
|
||||
def getMultipleActorsOneForOneConf: Supervisor = {
|
||||
pingpong1 = actorOf[PingPong1Actor].start
|
||||
pingpong2 = actorOf[PingPong2Actor].start
|
||||
pingpong3 = actorOf[PingPong3Actor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(OneForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
Supervise(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
Supervise(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil))
|
||||
}
|
||||
|
||||
def getNestedSupervisorsAllForOneConf: Supervisor = {
|
||||
pingpong1 = actorOf[PingPong1Actor].start
|
||||
pingpong2 = actorOf[PingPong2Actor].start
|
||||
pingpong3 = actorOf[PingPong3Actor].start
|
||||
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 5000, List(classOf[Exception])),
|
||||
Supervise(
|
||||
pingpong1,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 5000, Nil),
|
||||
Supervise(
|
||||
pingpong2,
|
||||
LifeCycle(Permanent))
|
||||
::
|
||||
Supervise(
|
||||
pingpong3,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil)
|
||||
:: Nil))
|
||||
}
|
||||
}
|
||||
173
akka-actors/src/test/scala/dataflow/DataFlowSpec.scala
Normal file
173
akka-actors/src/test/scala/dataflow/DataFlowSpec.scala
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.dataflow
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.Assertions
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
import java.util.concurrent.atomic.{AtomicLong, AtomicReference, AtomicInteger}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture
|
||||
import se.scalablesolutions.akka.actor.ActorRegistry
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class DataFlowTest extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
||||
describe("DataflowVariable") {
|
||||
it("should work and generate correct results") {
|
||||
import DataFlow._
|
||||
|
||||
val latch = new CountDownLatch(1)
|
||||
val result = new AtomicInteger(0)
|
||||
val x, y, z = new DataFlowVariable[Int]
|
||||
thread {
|
||||
z << x() + y()
|
||||
latch.countDown
|
||||
result.set(z())
|
||||
}
|
||||
thread { x << 40 }
|
||||
thread { y << 2 }
|
||||
|
||||
latch.await(3,TimeUnit.SECONDS) should equal (true)
|
||||
List(x,y,z).foreach(_.shutdown)
|
||||
result.get should equal (42)
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
it("should be able to transform a stream") {
|
||||
import DataFlow._
|
||||
|
||||
def ints(n: Int, max: Int): List[Int] =
|
||||
if (n == max) Nil
|
||||
else n :: ints(n + 1, max)
|
||||
|
||||
def sum(s: Int, stream: List[Int]): List[Int] = stream match {
|
||||
case Nil => s :: Nil
|
||||
case h :: t => s :: sum(h + s, t)
|
||||
}
|
||||
|
||||
val latch = new CountDownLatch(1)
|
||||
val result = new AtomicReference[List[Int]](Nil)
|
||||
val x = new DataFlowVariable[List[Int]]
|
||||
val y = new DataFlowVariable[List[Int]]
|
||||
val z = new DataFlowVariable[List[Int]]
|
||||
|
||||
thread { x << ints(0, 1000) }
|
||||
thread { y << sum(0, x()) }
|
||||
|
||||
thread { z << y()
|
||||
result.set(z())
|
||||
latch.countDown
|
||||
}
|
||||
|
||||
latch.await(3,TimeUnit.SECONDS) should equal (true)
|
||||
List(x,y,z).foreach(_.shutdown)
|
||||
result.get should equal (sum(0,ints(0,1000)))
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
}
|
||||
|
||||
/*it("should be able to join streams") {
|
||||
import DataFlow._
|
||||
|
||||
def ints(n: Int, max: Int, stream: DataFlowStream[Int]): Unit = if (n != max) {
|
||||
stream <<< n
|
||||
ints(n + 1, max, stream)
|
||||
}
|
||||
|
||||
def sum(s: Int, in: DataFlowStream[Int], out: DataFlowStream[Int]): Unit = {
|
||||
out <<< s
|
||||
sum(in() + s, in, out)
|
||||
}
|
||||
|
||||
val producer = new DataFlowStream[Int]
|
||||
val consumer = new DataFlowStream[Int]
|
||||
val latch = new CountDownLatch(1)
|
||||
val result = new AtomicInteger(0)
|
||||
|
||||
thread { ints(0, 1000, producer) }
|
||||
thread {
|
||||
Thread.sleep(1000)
|
||||
result.set(producer.map(x => x * x).foldLeft(0)(_ + _))
|
||||
latch.countDown
|
||||
}
|
||||
|
||||
latch.await(3,TimeUnit.SECONDS) should equal (true)
|
||||
result.get should equal (332833500)
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
it("should be able to sum streams recursively") {
|
||||
import DataFlow._
|
||||
|
||||
def ints(n: Int, max: Int, stream: DataFlowStream[Int]): Unit = if (n != max) {
|
||||
stream <<< n
|
||||
ints(n + 1, max, stream)
|
||||
}
|
||||
|
||||
def sum(s: Int, in: DataFlowStream[Int], out: DataFlowStream[Int]): Unit = {
|
||||
out <<< s
|
||||
sum(in() + s, in, out)
|
||||
}
|
||||
|
||||
val result = new AtomicLong(0)
|
||||
|
||||
val producer = new DataFlowStream[Int]
|
||||
val consumer = new DataFlowStream[Int]
|
||||
val latch = new CountDownLatch(1)
|
||||
|
||||
@tailrec def recurseSum(stream: DataFlowStream[Int]): Unit = {
|
||||
val x = stream()
|
||||
|
||||
if(result.addAndGet(x) == 166666500)
|
||||
latch.countDown
|
||||
|
||||
recurseSum(stream)
|
||||
}
|
||||
|
||||
thread { ints(0, 1000, producer) }
|
||||
thread { sum(0, producer, consumer) }
|
||||
thread { recurseSum(consumer) }
|
||||
|
||||
latch.await(15,TimeUnit.SECONDS) should equal (true)
|
||||
ActorRegistry.shutdownAll
|
||||
}*/
|
||||
|
||||
/* Test not ready for prime time, causes some sort of deadlock */
|
||||
/* it("should be able to conditionally set variables") {
|
||||
|
||||
import DataFlow._
|
||||
|
||||
val latch = new CountDownLatch(1)
|
||||
val x, y, z, v = new DataFlowVariable[Int]
|
||||
|
||||
val main = thread {
|
||||
x << 1
|
||||
z << Math.max(x(),y())
|
||||
latch.countDown
|
||||
}
|
||||
|
||||
val setY = thread {
|
||||
Thread sleep 2000
|
||||
y << 2
|
||||
}
|
||||
|
||||
val setV = thread {
|
||||
v << y
|
||||
}
|
||||
|
||||
latch.await(2,TimeUnit.SECONDS) should equal (true)
|
||||
List(x,y,z,v) foreach (_.shutdown)
|
||||
List(main,setY,setV) foreach (_ ! Exit)
|
||||
println("Foo")
|
||||
ActorRegistry.shutdownAll
|
||||
}*/
|
||||
}
|
||||
74
akka-actors/src/test/scala/dispatch/DispatchersSpec.scala
Normal file
74
akka-actors/src/test/scala/dispatch/DispatchersSpec.scala
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import net.lag.configgy.Config
|
||||
import scala.reflect.{Manifest}
|
||||
import se.scalablesolutions.akka.dispatch._
|
||||
|
||||
object DispatchersSpec {
|
||||
import Dispatchers._
|
||||
//
|
||||
val tipe = "type"
|
||||
val keepalivems = "keep-alive-ms"
|
||||
val corepoolsizefactor = "core-pool-size-factor"
|
||||
val maxpoolsizefactor = "max-pool-size-factor"
|
||||
val executorbounds = "executor-bounds"
|
||||
val allowcoretimeout = "allow-core-timeout"
|
||||
val rejectionpolicy = "rejection-policy" // abort, caller-runs, discard-oldest, discard
|
||||
val throughput = "throughput" // Throughput for ExecutorBasedEventDrivenDispatcher
|
||||
val aggregate = "aggregate" // Aggregate on/off for HawtDispatchers
|
||||
|
||||
def instance(dispatcher: MessageDispatcher): (MessageDispatcher) => Boolean = _ == dispatcher
|
||||
def ofType[T <: MessageDispatcher : Manifest]: (MessageDispatcher) => Boolean = _.getClass == manifest[T].erasure
|
||||
|
||||
def typesAndValidators: Map[String,(MessageDispatcher) => Boolean] = Map(
|
||||
"ReactorBasedSingleThreadEventDriven" -> ofType[ReactorBasedSingleThreadEventDrivenDispatcher],
|
||||
"ExecutorBasedEventDrivenWorkStealing" -> ofType[ExecutorBasedEventDrivenWorkStealingDispatcher],
|
||||
"ExecutorBasedEventDriven" -> ofType[ExecutorBasedEventDrivenDispatcher],
|
||||
"ReactorBasedThreadPoolEventDriven" -> ofType[ReactorBasedThreadPoolEventDrivenDispatcher],
|
||||
"Hawt" -> ofType[HawtDispatcher],
|
||||
"GlobalReactorBasedSingleThreadEventDriven" -> instance(globalReactorBasedSingleThreadEventDrivenDispatcher),
|
||||
"GlobalReactorBasedThreadPoolEventDriven" -> instance(globalReactorBasedThreadPoolEventDrivenDispatcher),
|
||||
"GlobalExecutorBasedEventDriven" -> instance(globalExecutorBasedEventDrivenDispatcher),
|
||||
"GlobalHawt" -> instance(globalHawtDispatcher)
|
||||
)
|
||||
|
||||
def validTypes = typesAndValidators.keys.toList
|
||||
|
||||
lazy val allDispatchers: Map[String,Option[MessageDispatcher]] = {
|
||||
validTypes.map(t => (t,from(Config.fromMap(Map(tipe -> t))))).toMap
|
||||
}
|
||||
}
|
||||
|
||||
class DispatchersSpec extends JUnitSuite {
|
||||
|
||||
import Dispatchers._
|
||||
import DispatchersSpec._
|
||||
|
||||
@Test def shouldYieldNoneIfTypeIsMissing {
|
||||
assert(from(Config.fromMap(Map())) === None)
|
||||
}
|
||||
|
||||
@Test(expected = classOf[IllegalArgumentException])
|
||||
def shouldThrowIllegalArgumentExceptionIfTypeDoesntExist {
|
||||
from(Config.fromMap(Map(tipe -> "typedoesntexist")))
|
||||
}
|
||||
|
||||
@Test def shouldGetTheCorrectTypesOfDispatchers {
|
||||
//It can create/obtain all defined types
|
||||
assert(allDispatchers.values.forall(_.isDefined))
|
||||
//All created/obtained dispatchers are of the expeced type/instance
|
||||
assert(typesAndValidators.forall( tuple => tuple._2(allDispatchers(tuple._1).get) ))
|
||||
}
|
||||
|
||||
@Test def defaultingToDefaultWhileLoadingTheDefaultShouldWork {
|
||||
assert(from(Config.fromMap(Map())).getOrElse(defaultGlobalDispatcher) == defaultGlobalDispatcher)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object ExecutorBasedEventDrivenDispatcherActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(self.uuid)
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
|
||||
object OneWayTestActor {
|
||||
val oneWay = new CountDownLatch(1)
|
||||
}
|
||||
class OneWayTestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(self.uuid)
|
||||
def receive = {
|
||||
case "OneWay" => OneWayTestActor.oneWay.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite {
|
||||
import ExecutorBasedEventDrivenDispatcherActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
@Test def shouldSendOneWay = {
|
||||
val actor = actorOf[OneWayTestActor].start
|
||||
val result = actor ! "OneWay"
|
||||
assert(OneWayTestActor.oneWay.await(1, TimeUnit.SECONDS))
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = (actor !! ("Hello", 10000)).as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start
|
||||
try {
|
||||
actor !! "Failure"
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e =>
|
||||
assert("Expected exception; to test fault-tolerance" === e.getMessage())
|
||||
}
|
||||
actor.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
/**
|
||||
* Tests the behaviour of the executor based event driven dispatcher when multiple actors are being dispatched on it.
|
||||
*
|
||||
* @author Jan Van Besien
|
||||
*/
|
||||
class ExecutorBasedEventDrivenDispatcherActorsSpec extends JUnitSuite with MustMatchers {
|
||||
class SlowActor(finishedCounter: CountDownLatch) extends Actor {
|
||||
self.id = "SlowActor"
|
||||
|
||||
def receive = {
|
||||
case x: Int => {
|
||||
Thread.sleep(50) // slow actor
|
||||
finishedCounter.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FastActor(finishedCounter: CountDownLatch) extends Actor {
|
||||
self.id = "FastActor"
|
||||
|
||||
def receive = {
|
||||
case x: Int => {
|
||||
finishedCounter.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test def slowActorShouldntBlockFastActor {
|
||||
val sFinished = new CountDownLatch(50)
|
||||
val fFinished = new CountDownLatch(10)
|
||||
val s = actorOf(new SlowActor(sFinished)).start
|
||||
val f = actorOf(new FastActor(fFinished)).start
|
||||
|
||||
// send a lot of stuff to s
|
||||
for (i <- 1 to 50) {
|
||||
s ! i
|
||||
}
|
||||
|
||||
// send some messages to f
|
||||
for (i <- 1 to 10) {
|
||||
f ! i
|
||||
}
|
||||
|
||||
// now assert that f is finished while s is still busy
|
||||
fFinished.await
|
||||
assert(sFinished.getCount > 0)
|
||||
sFinished.await
|
||||
assert(sFinished.getCount === 0)
|
||||
f.stop
|
||||
s.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
|
||||
import java.util.concurrent.{TimeUnit, CountDownLatch}
|
||||
import se.scalablesolutions.akka.actor.{IllegalActorStateException, Actor}
|
||||
import Actor._
|
||||
|
||||
object ExecutorBasedEventDrivenWorkStealingDispatcherSpec {
|
||||
val delayableActorDispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher")
|
||||
val sharedActorDispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher")
|
||||
val parentActorDispatcher = Dispatchers.newExecutorBasedEventDrivenWorkStealingDispatcher("pooled-dispatcher")
|
||||
|
||||
class DelayableActor(name: String, delay: Int, finishedCounter: CountDownLatch) extends Actor {
|
||||
self.dispatcher = delayableActorDispatcher
|
||||
var invocationCount = 0
|
||||
self.id = name
|
||||
|
||||
def receive = {
|
||||
case x: Int => {
|
||||
Thread.sleep(delay)
|
||||
invocationCount += 1
|
||||
finishedCounter.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FirstActor extends Actor {
|
||||
self.dispatcher = sharedActorDispatcher
|
||||
def receive = {case _ => {}}
|
||||
}
|
||||
|
||||
class SecondActor extends Actor {
|
||||
self.dispatcher = sharedActorDispatcher
|
||||
def receive = {case _ => {}}
|
||||
}
|
||||
|
||||
class ParentActor extends Actor {
|
||||
self.dispatcher = parentActorDispatcher
|
||||
def receive = {case _ => {}}
|
||||
}
|
||||
|
||||
class ChildActor extends ParentActor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jan Van Besien
|
||||
*/
|
||||
class ExecutorBasedEventDrivenWorkStealingDispatcherSpec extends JUnitSuite with MustMatchers {
|
||||
import ExecutorBasedEventDrivenWorkStealingDispatcherSpec._
|
||||
|
||||
@Test def fastActorShouldStealWorkFromSlowActor {
|
||||
val finishedCounter = new CountDownLatch(110)
|
||||
|
||||
val slow = actorOf(new DelayableActor("slow", 50, finishedCounter)).start
|
||||
val fast = actorOf(new DelayableActor("fast", 10, finishedCounter)).start
|
||||
|
||||
for (i <- 1 to 100) {
|
||||
// send most work to slow actor
|
||||
if (i % 20 == 0)
|
||||
fast ! i
|
||||
else
|
||||
slow ! i
|
||||
}
|
||||
|
||||
// now send some messages to actors to keep the dispatcher dispatching messages
|
||||
for (i <- 1 to 10) {
|
||||
Thread.sleep(150)
|
||||
if (i % 2 == 0)
|
||||
fast ! i
|
||||
else
|
||||
slow ! i
|
||||
}
|
||||
|
||||
finishedCounter.await(5, TimeUnit.SECONDS)
|
||||
fast.actor.asInstanceOf[DelayableActor].invocationCount must be >
|
||||
(slow.actor.asInstanceOf[DelayableActor].invocationCount)
|
||||
slow.stop
|
||||
fast.stop
|
||||
}
|
||||
|
||||
@Test def canNotUseActorsOfDifferentTypesInSameDispatcher(): Unit = {
|
||||
val first = actorOf[FirstActor]
|
||||
val second = actorOf[SecondActor]
|
||||
|
||||
first.start
|
||||
intercept[IllegalActorStateException] {
|
||||
second.start
|
||||
}
|
||||
}
|
||||
|
||||
@Test def canNotUseActorsOfDifferentSubTypesInSameDispatcher(): Unit = {
|
||||
val parent = actorOf[ParentActor]
|
||||
val child = actorOf[ChildActor]
|
||||
|
||||
parent.start
|
||||
intercept[IllegalActorStateException] {
|
||||
child.start
|
||||
}
|
||||
}
|
||||
}
|
||||
106
akka-actors/src/test/scala/dispatch/FutureSpec.scala
Normal file
106
akka-actors/src/test/scala/dispatch/FutureSpec.scala
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import se.scalablesolutions.akka.dispatch.Futures
|
||||
import Actor._
|
||||
|
||||
object FutureSpec {
|
||||
class TestActor extends Actor {
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "NoReply" => {}
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FutureSpec extends JUnitSuite {
|
||||
import FutureSpec._
|
||||
|
||||
@Test def shouldActorReplyResultThroughExplicitFuture {
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val future = actor !!! "Hello"
|
||||
future.await
|
||||
assert(future.result.isDefined)
|
||||
assert("World" === future.result.get)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldActorReplyExceptionThroughExplicitFuture {
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val future = actor !!! "Failure"
|
||||
future.await
|
||||
assert(future.exception.isDefined)
|
||||
assert("Expected exception; to test fault-tolerance" === future.exception.get.getMessage)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
/*
|
||||
// FIXME: implement Futures.awaitEither, and uncomment these two tests
|
||||
@Test def shouldFutureAwaitEitherLeft = {
|
||||
val actor1 = actorOf[TestActor].start
|
||||
val actor2 = actorOf[TestActor].start
|
||||
val future1 = actor1 !!! "Hello"
|
||||
val future2 = actor2 !!! "NoReply"
|
||||
val result = Futures.awaitEither(future1, future2)
|
||||
assert(result.isDefined)
|
||||
assert("World" === result.get)
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldFutureAwaitEitherRight = {
|
||||
val actor1 = actorOf[TestActor].start
|
||||
val actor2 = actorOf[TestActor].start
|
||||
val future1 = actor1 !!! "NoReply"
|
||||
val future2 = actor2 !!! "Hello"
|
||||
val result = Futures.awaitEither(future1, future2)
|
||||
assert(result.isDefined)
|
||||
assert("World" === result.get)
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
*/
|
||||
@Test def shouldFutureAwaitOneLeft = {
|
||||
val actor1 = actorOf[TestActor].start
|
||||
val actor2 = actorOf[TestActor].start
|
||||
val future1 = actor1 !!! "NoReply"
|
||||
val future2 = actor2 !!! "Hello"
|
||||
val result = Futures.awaitOne(List(future1, future2))
|
||||
assert(result.result.isDefined)
|
||||
assert("World" === result.result.get)
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldFutureAwaitOneRight = {
|
||||
val actor1 = actorOf[TestActor].start
|
||||
val actor2 = actorOf[TestActor].start
|
||||
val future1 = actor1 !!! "Hello"
|
||||
val future2 = actor2 !!! "NoReply"
|
||||
val result = Futures.awaitOne(List(future1, future2))
|
||||
assert(result.result.isDefined)
|
||||
assert("World" === result.result.get)
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldFutureAwaitAll = {
|
||||
val actor1 = actorOf[TestActor].start
|
||||
val actor2 = actorOf[TestActor].start
|
||||
val future1 = actor1 !!! "Hello"
|
||||
val future2 = actor2 !!! "Hello"
|
||||
Futures.awaitAll(List(future1, future2))
|
||||
assert(future1.result.isDefined)
|
||||
assert("World" === future1.result.get)
|
||||
assert(future2.result.isDefined)
|
||||
assert("World" === future2.result.get)
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.{HawtDispatcher, Dispatchers}
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object HawtDispatcherActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = new HawtDispatcher()
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
|
||||
object OneWayTestActor {
|
||||
val oneWay = new CountDownLatch(1)
|
||||
}
|
||||
class OneWayTestActor extends Actor {
|
||||
self.dispatcher = new HawtDispatcher()
|
||||
def receive = {
|
||||
case "OneWay" => OneWayTestActor.oneWay.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HawtDispatcherActorSpec extends JUnitSuite {
|
||||
import HawtDispatcherActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
@Test def shouldSendOneWay = {
|
||||
val actor = actorOf[OneWayTestActor].start
|
||||
val result = actor ! "OneWay"
|
||||
assert(OneWayTestActor.oneWay.await(1, TimeUnit.SECONDS))
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = (actor !! ("Hello", 10000)).as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start
|
||||
try {
|
||||
actor !! "Failure"
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e =>
|
||||
assert("Expected exception; to test fault-tolerance" === e.getMessage())
|
||||
}
|
||||
actor.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.net.InetSocketAddress
|
||||
import java.io.IOException
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.{SocketChannel, SelectionKey, ServerSocketChannel}
|
||||
|
||||
import se.scalablesolutions.akka.actor._
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.dispatch.HawtDispatcher
|
||||
|
||||
import org.fusesource.hawtdispatch.DispatchSource
|
||||
import org.fusesource.hawtdispatch.ScalaDispatch._
|
||||
|
||||
/**
|
||||
* This is an example of how to crate an Akka actor based TCP echo server using
|
||||
* the HawtDispatch dispatcher and NIO event sources.
|
||||
*/
|
||||
object HawtDispatcherEchoServer {
|
||||
|
||||
private val hawt = new HawtDispatcher
|
||||
var port=4444;
|
||||
var useReactorPattern=true
|
||||
|
||||
def main(args:Array[String]):Unit = run
|
||||
|
||||
def run() = {
|
||||
val server = actorOf(new Server(port))
|
||||
server.start
|
||||
Scheduler.schedule(server, DisplayStats, 1, 5, TimeUnit.SECONDS)
|
||||
|
||||
println("Press enter to shutdown.");
|
||||
System.in.read
|
||||
server ! Shutdown
|
||||
}
|
||||
|
||||
case object Shutdown
|
||||
case object DisplayStats
|
||||
case class SessionClosed(session:ActorRef)
|
||||
|
||||
class Server(val port: Int) extends Actor {
|
||||
|
||||
self.dispatcher = hawt
|
||||
|
||||
var channel:ServerSocketChannel = _
|
||||
var accept_source:DispatchSource = _
|
||||
var sessions = ListBuffer[ActorRef]()
|
||||
|
||||
override def init = {
|
||||
channel = ServerSocketChannel.open();
|
||||
channel.socket().bind(new InetSocketAddress(port));
|
||||
channel.configureBlocking(false);
|
||||
|
||||
// Setup the accept source, it will callback to the handler methods
|
||||
// via the actor's mailbox so you don't need to worry about
|
||||
// synchronizing with the local variables
|
||||
accept_source = createSource(channel, SelectionKey.OP_ACCEPT, HawtDispatcher.queue(self));
|
||||
accept_source.setEventHandler(^{ accept });
|
||||
accept_source.setDisposer(^{
|
||||
channel.close();
|
||||
println("Closed port: "+port);
|
||||
});
|
||||
|
||||
accept_source.resume
|
||||
|
||||
println("Listening on port: "+port);
|
||||
}
|
||||
|
||||
|
||||
private def accept() = {
|
||||
var socket = channel.accept();
|
||||
while( socket!=null ) {
|
||||
try {
|
||||
socket.configureBlocking(false);
|
||||
val session = actorOf(new Session(self, socket))
|
||||
session.start()
|
||||
sessions += session
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
socket.close
|
||||
}
|
||||
socket = channel.accept();
|
||||
}
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case SessionClosed(session) =>
|
||||
sessions = sessions.filterNot( _ == session )
|
||||
session.stop
|
||||
case DisplayStats =>
|
||||
sessions.foreach { session=>
|
||||
session ! DisplayStats
|
||||
}
|
||||
case Shutdown =>
|
||||
sessions.foreach { session=>
|
||||
session.stop
|
||||
}
|
||||
sessions.clear
|
||||
accept_source.release
|
||||
self.stop
|
||||
}
|
||||
}
|
||||
|
||||
class Session(val server:ActorRef, val channel: SocketChannel) extends Actor {
|
||||
|
||||
self.dispatcher = hawt
|
||||
|
||||
val buffer = ByteBuffer.allocate(1024);
|
||||
val remote_address = channel.socket.getRemoteSocketAddress.toString
|
||||
|
||||
var read_source:DispatchSource = _
|
||||
var write_source:DispatchSource = _
|
||||
|
||||
var readCounter = 0L
|
||||
var writeCounter = 0L
|
||||
var closed = false
|
||||
|
||||
override def init = {
|
||||
|
||||
if(useReactorPattern) {
|
||||
// Then we will be using the reactor pattern for handling IO:
|
||||
// Pin this actor to a single thread. The read/write event sources will poll
|
||||
// a Selector on the pinned thread. Since the IO events are generated on the same
|
||||
// thread as where the Actor is pinned to, it can avoid a substantial amount
|
||||
// thread synchronization. Plus your GC will perform better since all the IO
|
||||
// processing is done on a single thread.
|
||||
HawtDispatcher.pin(self)
|
||||
} else {
|
||||
// Then we will be using sing the proactor pattern for handling IO:
|
||||
// Then the actor will not be pinned to a specific thread. The read/write
|
||||
// event sources will poll a Selector and then asynchronously dispatch the
|
||||
// event's to the actor via the thread pool.
|
||||
}
|
||||
|
||||
// Setup the sources, they will callback to the handler methods
|
||||
// via the actor's mailbox so you don't need to worry about
|
||||
// synchronizing with the local variables
|
||||
read_source = createSource(channel, SelectionKey.OP_READ, HawtDispatcher.queue(self));
|
||||
read_source.setEventHandler(^{ read })
|
||||
read_source.setCancelHandler(^{ close })
|
||||
|
||||
write_source = createSource(channel, SelectionKey.OP_READ, HawtDispatcher.queue(self));
|
||||
write_source.setEventHandler(^{ write })
|
||||
write_source.setCancelHandler(^{ close })
|
||||
|
||||
read_source.resume
|
||||
println("Accepted connection from: "+remote_address);
|
||||
}
|
||||
|
||||
override def shutdown = {
|
||||
closed = true
|
||||
read_source.release
|
||||
write_source.release
|
||||
channel.close
|
||||
}
|
||||
|
||||
private def catchio(func: =>Unit):Unit = {
|
||||
try {
|
||||
func
|
||||
} catch {
|
||||
case e:IOException => close
|
||||
}
|
||||
}
|
||||
|
||||
def read():Unit = catchio {
|
||||
channel.read(buffer) match {
|
||||
case -1 =>
|
||||
close // peer disconnected.
|
||||
case 0 =>
|
||||
case count:Int =>
|
||||
readCounter += count
|
||||
buffer.flip;
|
||||
read_source.suspend
|
||||
write_source.resume
|
||||
write()
|
||||
}
|
||||
}
|
||||
|
||||
def write() = catchio {
|
||||
writeCounter += channel.write(buffer)
|
||||
if (buffer.remaining == 0) {
|
||||
buffer.clear
|
||||
write_source.suspend
|
||||
read_source.resume
|
||||
}
|
||||
}
|
||||
|
||||
def close() = {
|
||||
if( !closed ) {
|
||||
closed = true
|
||||
server ! SessionClosed(self)
|
||||
}
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case DisplayStats =>
|
||||
println("connection to %s reads: %,d bytes, writes: %,d".format(remote_address, readCounter, writeCounter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object ReactorBasedSingleThreadEventDrivenDispatcherActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newReactorBasedSingleThreadEventDrivenDispatcher(self.uuid)
|
||||
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
|
||||
object OneWayTestActor {
|
||||
val oneWay = new CountDownLatch(1)
|
||||
}
|
||||
class OneWayTestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher(self.uuid)
|
||||
def receive = {
|
||||
case "OneWay" => OneWayTestActor.oneWay.countDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReactorBasedSingleThreadEventDrivenDispatcherActorSpec extends JUnitSuite {
|
||||
import ReactorBasedSingleThreadEventDrivenDispatcherActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
@Test def shouldSendOneWay = {
|
||||
val actor = actorOf[OneWayTestActor].start
|
||||
val result = actor ! "OneWay"
|
||||
assert(OneWayTestActor.oneWay.await(1, TimeUnit.SECONDS))
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = (actor !! ("Hello", 10000)).as[String].get
|
||||
assert("World" === result)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start
|
||||
try {
|
||||
actor !! "Failure"
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e =>
|
||||
assert("Expected exception; to test fault-tolerance" === e.getMessage())
|
||||
}
|
||||
actor.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object ReactorBasedThreadPoolEventDrivenDispatcherActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newReactorBasedThreadPoolEventDrivenDispatcher(self.uuid)
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReactorBasedThreadPoolEventDrivenDispatcherActorSpec extends JUnitSuite {
|
||||
import ReactorBasedThreadPoolEventDrivenDispatcherActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
@Test def shouldSendOneWay {
|
||||
val oneWay = new CountDownLatch(1)
|
||||
val actor = actorOf(new Actor {
|
||||
self.dispatcher = Dispatchers.newReactorBasedThreadPoolEventDrivenDispatcher(self.uuid)
|
||||
def receive = {
|
||||
case "OneWay" => oneWay.countDown
|
||||
}
|
||||
}).start
|
||||
val result = actor ! "OneWay"
|
||||
assert(oneWay.await(1, TimeUnit.SECONDS))
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = (actor !! ("Hello", 10000)).as[String].get
|
||||
assert("World" === result)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start
|
||||
try {
|
||||
actor !! "Failure"
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e =>
|
||||
assert("Expected exception; to test fault-tolerance" === e.getMessage())
|
||||
}
|
||||
actor.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package se.scalablesolutions.akka.actor.dispatch
|
||||
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
|
||||
import se.scalablesolutions.akka.dispatch.Dispatchers
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
object ThreadBasedActorSpec {
|
||||
class TestActor extends Actor {
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self)
|
||||
|
||||
def receive = {
|
||||
case "Hello" =>
|
||||
self.reply("World")
|
||||
case "Failure" =>
|
||||
throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadBasedActorSpec extends JUnitSuite {
|
||||
import ThreadBasedActorSpec._
|
||||
|
||||
private val unit = TimeUnit.MILLISECONDS
|
||||
|
||||
@Test def shouldSendOneWay {
|
||||
var oneWay = new CountDownLatch(1)
|
||||
val actor = actorOf(new Actor {
|
||||
self.dispatcher = Dispatchers.newThreadBasedDispatcher(self)
|
||||
def receive = {
|
||||
case "OneWay" => oneWay.countDown
|
||||
}
|
||||
}).start
|
||||
val result = actor ! "OneWay"
|
||||
assert(oneWay.await(1, TimeUnit.SECONDS))
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplySync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = (actor !! ("Hello", 10000)).as[String]
|
||||
assert("World" === result.get)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReplyAsync = {
|
||||
val actor = actorOf[TestActor].start
|
||||
val result = actor !! "Hello"
|
||||
assert("World" === result.get.asInstanceOf[String])
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldSendReceiveException = {
|
||||
val actor = actorOf[TestActor].start
|
||||
try {
|
||||
actor !! "Failure"
|
||||
fail("Should have thrown an exception")
|
||||
} catch {
|
||||
case e =>
|
||||
assert("Expected exception; to test fault-tolerance" === e.getMessage())
|
||||
}
|
||||
actor.stop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package se.scalablesolutions.akka.dispatch
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.locks.Lock
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.{Test, Before}
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import Actor._
|
||||
|
||||
// FIXME use this test when we have removed the MessageInvoker classes
|
||||
/*
|
||||
class ThreadBasedDispatcherSpec extends JUnitSuite {
|
||||
private var threadingIssueDetected: AtomicBoolean = null
|
||||
val key1 = actorOf(new Actor { def receive = { case _ => {}} })
|
||||
val key2 = actorOf(new Actor { def receive = { case _ => {}} })
|
||||
val key3 = actorOf(new Actor { def receive = { case _ => {}} })
|
||||
|
||||
class TestMessageHandle(handleLatch: CountDownLatch) extends MessageInvoker {
|
||||
val guardLock: Lock = new ReentrantLock
|
||||
|
||||
def invoke(message: MessageInvocation) {
|
||||
try {
|
||||
if (threadingIssueDetected.get) return
|
||||
if (guardLock.tryLock) {
|
||||
handleLatch.countDown
|
||||
} else {
|
||||
threadingIssueDetected.set(true)
|
||||
}
|
||||
} catch {
|
||||
case e: Exception => threadingIssueDetected.set(true)
|
||||
} finally {
|
||||
guardLock.unlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
def setUp = {
|
||||
threadingIssueDetected = new AtomicBoolean(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldMessagesDispatchedToTheSameHandlerAreExecutedSequentially = {
|
||||
internalTestMessagesDispatchedToTheSameHandlerAreExecutedSequentially
|
||||
}
|
||||
|
||||
@Test
|
||||
def shouldMessagesDispatchedToHandlersAreExecutedInFIFOOrder = {
|
||||
internalTestMessagesDispatchedToHandlersAreExecutedInFIFOOrder
|
||||
}
|
||||
|
||||
private def internalTestMessagesDispatchedToTheSameHandlerAreExecutedSequentially(): Unit = {
|
||||
val guardLock = new ReentrantLock
|
||||
val handleLatch = new CountDownLatch(100)
|
||||
val dispatcher = new ThreadBasedDispatcher("name", new TestMessageHandle(handleLatch))
|
||||
dispatcher.start
|
||||
for (i <- 0 until 100) {
|
||||
dispatcher.dispatch(new MessageInvocation(key1, new Object, None, None))
|
||||
}
|
||||
assert(handleLatch.await(5, TimeUnit.SECONDS))
|
||||
assert(!threadingIssueDetected.get)
|
||||
}
|
||||
|
||||
private def internalTestMessagesDispatchedToHandlersAreExecutedInFIFOOrder(): Unit = {
|
||||
val handleLatch = new CountDownLatch(100)
|
||||
val dispatcher = new ThreadBasedDispatcher("name", new MessageInvoker {
|
||||
var currentValue = -1;
|
||||
def invoke(message: MessageInvocation) {
|
||||
if (threadingIssueDetected.get) return
|
||||
val messageValue = message.message.asInstanceOf[Int]
|
||||
if (messageValue.intValue == currentValue + 1) {
|
||||
currentValue = messageValue.intValue
|
||||
handleLatch.countDown
|
||||
} else threadingIssueDetected.set(true)
|
||||
}
|
||||
})
|
||||
dispatcher.start
|
||||
for (i <- 0 until 100) {
|
||||
dispatcher.dispatch(new MessageInvocation(key1, i, None, None))
|
||||
}
|
||||
assert(handleLatch.await(5, TimeUnit.SECONDS))
|
||||
assert(!threadingIssueDetected.get)
|
||||
dispatcher.shutdown
|
||||
}
|
||||
}
|
||||
*/
|
||||
255
akka-actors/src/test/scala/misc/ActorRegistrySpec.scala
Normal file
255
akka-actors/src/test/scala/misc/ActorRegistrySpec.scala
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import org.junit.Test
|
||||
import Actor._
|
||||
import java.util.concurrent.{CyclicBarrier, TimeUnit, CountDownLatch}
|
||||
|
||||
object ActorRegistrySpec {
|
||||
var record = ""
|
||||
class TestActor extends Actor {
|
||||
self.id = "MyID"
|
||||
def receive = {
|
||||
case "ping" =>
|
||||
record = "pong" + record
|
||||
self.reply("got ping")
|
||||
}
|
||||
}
|
||||
|
||||
class TestActor2 extends Actor {
|
||||
self.id = "MyID2"
|
||||
def receive = {
|
||||
case "ping" =>
|
||||
record = "pong" + record
|
||||
self.reply("got ping")
|
||||
case "ping2" =>
|
||||
record = "pong" + record
|
||||
self.reply("got ping")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ActorRegistrySpec extends JUnitSuite {
|
||||
import ActorRegistrySpec._
|
||||
|
||||
@Test def shouldGetActorByIdFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val actors = ActorRegistry.actorsFor("MyID")
|
||||
assert(actors.size === 1)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorByUUIDFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor = actorOf[TestActor]
|
||||
val uuid = actor.uuid
|
||||
actor.start
|
||||
val actorOrNone = ActorRegistry.actorFor(uuid)
|
||||
assert(actorOrNone.isDefined)
|
||||
assert(actorOrNone.get.uuid === uuid)
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorByClassFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val actors = ActorRegistry.actorsFor(classOf[TestActor])
|
||||
assert(actors.size === 1)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorByManifestFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val actors = ActorRegistry.actorsFor[TestActor]
|
||||
assert(actors.size === 1)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldFindThingsFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor = actorOf[TestActor]
|
||||
actor.start
|
||||
val found = ActorRegistry.find({ case a: ActorRef if a.actor.isInstanceOf[TestActor] => a })
|
||||
assert(found.isDefined)
|
||||
assert(found.get.actor.isInstanceOf[TestActor])
|
||||
assert(found.get.id === "MyID")
|
||||
actor.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorsByIdFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
val actors = ActorRegistry.actorsFor("MyID")
|
||||
assert(actors.size === 2)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
assert(actors.last.actor.isInstanceOf[TestActor])
|
||||
assert(actors.last.id === "MyID")
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorsByClassFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
val actors = ActorRegistry.actorsFor(classOf[TestActor])
|
||||
assert(actors.size === 2)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
assert(actors.last.actor.isInstanceOf[TestActor])
|
||||
assert(actors.last.id === "MyID")
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorsByManifestFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
val actors = ActorRegistry.actorsFor[TestActor]
|
||||
assert(actors.size === 2)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
assert(actors.last.actor.isInstanceOf[TestActor])
|
||||
assert(actors.last.id === "MyID")
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetActorsByMessageFromActorRegistry {
|
||||
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor2]
|
||||
actor2.start
|
||||
|
||||
val actorsForAcotrTestActor = ActorRegistry.actorsFor[TestActor]
|
||||
assert(actorsForAcotrTestActor.size === 1)
|
||||
|
||||
val actorsForAcotrTestActor2 = ActorRegistry.actorsFor[TestActor2]
|
||||
assert(actorsForAcotrTestActor2.size === 1)
|
||||
|
||||
val actorsForAcotr = ActorRegistry.actorsFor[Actor]
|
||||
assert(actorsForAcotr.size === 2)
|
||||
|
||||
|
||||
val actorsForMessagePing2 = ActorRegistry.actorsFor[Actor]("ping2")
|
||||
assert(actorsForMessagePing2.size === 1)
|
||||
|
||||
val actorsForMessagePing = ActorRegistry.actorsFor[Actor]("ping")
|
||||
assert(actorsForMessagePing.size === 2)
|
||||
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetAllActorsFromActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
val actors = ActorRegistry.actors
|
||||
assert(actors.size === 2)
|
||||
assert(actors.head.actor.isInstanceOf[TestActor])
|
||||
assert(actors.head.id === "MyID")
|
||||
assert(actors.last.actor.isInstanceOf[TestActor])
|
||||
assert(actors.last.id === "MyID")
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldGetResponseByAllActorsInActorRegistryWhenInvokingForeach {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
record = ""
|
||||
ActorRegistry.foreach(actor => actor !! "ping")
|
||||
assert(record === "pongpong")
|
||||
actor1.stop
|
||||
actor2.stop
|
||||
}
|
||||
|
||||
@Test def shouldShutdownAllActorsInActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
ActorRegistry.shutdownAll
|
||||
assert(ActorRegistry.actors.size === 0)
|
||||
}
|
||||
|
||||
@Test def shouldRemoveUnregisterActorInActorRegistry {
|
||||
ActorRegistry.shutdownAll
|
||||
val actor1 = actorOf[TestActor]
|
||||
actor1.start
|
||||
val actor2 = actorOf[TestActor]
|
||||
actor2.start
|
||||
assert(ActorRegistry.actors.size === 2)
|
||||
ActorRegistry.unregister(actor1)
|
||||
assert(ActorRegistry.actors.size === 1)
|
||||
ActorRegistry.unregister(actor2)
|
||||
assert(ActorRegistry.actors.size === 0)
|
||||
}
|
||||
|
||||
@Test def shouldBeAbleToRegisterActorsConcurrently {
|
||||
ActorRegistry.shutdownAll
|
||||
|
||||
val latch = new CountDownLatch(3)
|
||||
val barrier = new CyclicBarrier(3)
|
||||
|
||||
def mkTestActor(i:Int) = actorOf( new Actor {
|
||||
self.id = i.toString
|
||||
def receive = { case _ => }
|
||||
})
|
||||
|
||||
def mkTestActors = for(i <- 1 to 10;j <- 1 to 1000) yield mkTestActor(i)
|
||||
|
||||
def mkThread(actors: Iterable[ActorRef]) = new Thread {
|
||||
start
|
||||
override def run {
|
||||
barrier.await
|
||||
actors foreach { _.start }
|
||||
latch.countDown
|
||||
}
|
||||
}
|
||||
|
||||
val testActors1 = mkTestActors
|
||||
val testActors2 = mkTestActors
|
||||
val testActors3 = mkTestActors
|
||||
|
||||
mkThread(testActors1)
|
||||
mkThread(testActors2)
|
||||
mkThread(testActors3)
|
||||
|
||||
assert(latch.await(30,TimeUnit.SECONDS) === true)
|
||||
|
||||
for(i <- 1 to 10) {
|
||||
assert(ActorRegistry.actorsFor(i.toString).length === 3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
127
akka-actors/src/test/scala/misc/SchedulerSpec.scala
Normal file
127
akka-actors/src/test/scala/misc/SchedulerSpec.scala
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import Actor._
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import org.multiverse.api.latches.StandardLatch
|
||||
import org.junit.Test
|
||||
|
||||
class SchedulerSpec extends JUnitSuite {
|
||||
|
||||
def withCleanEndState(action: => Unit) {
|
||||
action
|
||||
Scheduler.restart
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
|
||||
@Test def schedulerShouldScheduleMoreThanOnce = withCleanEndState {
|
||||
|
||||
case object Tick
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val tickActor = actor {
|
||||
case Tick => countDownLatch.countDown
|
||||
}
|
||||
// run every 50 millisec
|
||||
Scheduler.schedule(tickActor, Tick, 0, 50, TimeUnit.MILLISECONDS)
|
||||
|
||||
// after max 1 second it should be executed at least the 3 times already
|
||||
assert(countDownLatch.await(1, TimeUnit.SECONDS))
|
||||
|
||||
val countDownLatch2 = new CountDownLatch(3)
|
||||
|
||||
Scheduler.schedule( () => countDownLatch2.countDown, 0, 50, TimeUnit.MILLISECONDS)
|
||||
|
||||
// after max 1 second it should be executed at least the 3 times already
|
||||
assert(countDownLatch2.await(1, TimeUnit.SECONDS))
|
||||
}
|
||||
|
||||
@Test def schedulerShouldScheduleOnce = withCleanEndState {
|
||||
case object Tick
|
||||
val countDownLatch = new CountDownLatch(3)
|
||||
val tickActor = actor {
|
||||
case Tick => countDownLatch.countDown
|
||||
}
|
||||
// run every 50 millisec
|
||||
Scheduler.scheduleOnce(tickActor, Tick, 50, TimeUnit.MILLISECONDS)
|
||||
Scheduler.scheduleOnce( () => countDownLatch.countDown, 50, TimeUnit.MILLISECONDS)
|
||||
|
||||
// after 1 second the wait should fail
|
||||
assert(countDownLatch.await(1, TimeUnit.SECONDS) == false)
|
||||
// should still be 1 left
|
||||
assert(countDownLatch.getCount == 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* ticket #372
|
||||
*/
|
||||
@Test def schedulerShouldntCreateActors = withCleanEndState {
|
||||
object Ping
|
||||
val ticks = new CountDownLatch(1000)
|
||||
val actor = actorOf(new Actor {
|
||||
def receive = { case Ping => ticks.countDown }
|
||||
}).start
|
||||
val numActors = ActorRegistry.actors.length
|
||||
(1 to 1000).foreach( _ => Scheduler.scheduleOnce(actor,Ping,1,TimeUnit.MILLISECONDS) )
|
||||
assert(ticks.await(10,TimeUnit.SECONDS))
|
||||
assert(ActorRegistry.actors.length === numActors)
|
||||
}
|
||||
|
||||
/**
|
||||
* ticket #372
|
||||
*/
|
||||
@Test def schedulerShouldBeCancellable = withCleanEndState {
|
||||
object Ping
|
||||
val ticks = new CountDownLatch(1)
|
||||
|
||||
val actor = actorOf(new Actor {
|
||||
def receive = { case Ping => ticks.countDown }
|
||||
}).start
|
||||
|
||||
(1 to 10).foreach { i =>
|
||||
val future = Scheduler.scheduleOnce(actor,Ping,1,TimeUnit.SECONDS)
|
||||
future.cancel(true)
|
||||
}
|
||||
assert(ticks.await(3,TimeUnit.SECONDS) == false) //No counting down should've been made
|
||||
}
|
||||
|
||||
/**
|
||||
* ticket #307
|
||||
*/
|
||||
@Test def actorRestartShouldPickUpScheduleAgain = withCleanEndState {
|
||||
|
||||
object Ping
|
||||
object Crash
|
||||
|
||||
val restartLatch = new StandardLatch
|
||||
val pingLatch = new CountDownLatch(6)
|
||||
|
||||
val actor = actorOf(new Actor {
|
||||
self.lifeCycle = Some(LifeCycle(Permanent))
|
||||
|
||||
def receive = {
|
||||
case Ping => pingLatch.countDown
|
||||
case Crash => throw new Exception("CRASH")
|
||||
}
|
||||
|
||||
override def postRestart(reason: Throwable) = restartLatch.open
|
||||
})
|
||||
Supervisor(
|
||||
SupervisorConfig(
|
||||
RestartStrategy(AllForOne, 3, 1000,
|
||||
List(classOf[Exception])),
|
||||
Supervise(
|
||||
actor,
|
||||
LifeCycle(Permanent))
|
||||
:: Nil)).start
|
||||
|
||||
Scheduler.schedule(actor, Ping, 500, 500, TimeUnit.MILLISECONDS)
|
||||
// appx 2 pings before crash
|
||||
Scheduler.scheduleOnce(actor, Crash, 1000, TimeUnit.MILLISECONDS)
|
||||
|
||||
assert(restartLatch.tryAwait(2, TimeUnit.SECONDS))
|
||||
// should be enough time for the ping countdown to recover and reach 6 pings
|
||||
assert(pingLatch.await(4, TimeUnit.SECONDS))
|
||||
}
|
||||
}
|
||||
179
akka-actors/src/test/scala/routing/RoutingSpec.scala
Normal file
179
akka-actors/src/test/scala/routing/RoutingSpec.scala
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package se.scalablesolutions.akka.actor.routing
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
|
||||
import org.scalatest.Suite
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import org.junit.Test
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.{CountDownLatch, TimeUnit}
|
||||
import se.scalablesolutions.akka.routing._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class RoutingSpec extends junit.framework.TestCase with Suite with MustMatchers with Logging {
|
||||
import Routing._
|
||||
|
||||
@Test def testDispatcher = {
|
||||
val (testMsg1,testMsg2,testMsg3,testMsg4) = ("test1","test2","test3","test4")
|
||||
val targetOk = new AtomicInteger(0)
|
||||
val t1 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg1` => self.reply(3)
|
||||
case `testMsg2` => self.reply(7)
|
||||
}
|
||||
} ).start
|
||||
|
||||
val t2 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg3` => self.reply(11)
|
||||
}
|
||||
}).start
|
||||
|
||||
val d = dispatcherActor {
|
||||
case `testMsg1`|`testMsg2` => t1
|
||||
case `testMsg3` => t2
|
||||
}.start
|
||||
|
||||
val result = for {
|
||||
a <- (d !! (testMsg1, 5000)).as[Int]
|
||||
b <- (d !! (testMsg2, 5000)).as[Int]
|
||||
c <- (d !! (testMsg3, 5000)).as[Int]
|
||||
} yield a + b + c
|
||||
|
||||
result.isDefined must be (true)
|
||||
result.get must be(21)
|
||||
|
||||
for(a <- List(t1,t2,d)) a.stop
|
||||
}
|
||||
|
||||
@Test def testLogger = {
|
||||
val msgs = new java.util.concurrent.ConcurrentSkipListSet[Any]
|
||||
val latch = new CountDownLatch(2)
|
||||
val t1 = actor {
|
||||
case _ =>
|
||||
}
|
||||
val l = loggerActor(t1,(x) => { msgs.add(x); latch.countDown }).start
|
||||
val foo : Any = "foo"
|
||||
val bar : Any = "bar"
|
||||
l ! foo
|
||||
l ! bar
|
||||
val done = latch.await(5,TimeUnit.SECONDS)
|
||||
done must be (true)
|
||||
msgs must ( have size (2) and contain (foo) and contain (bar) )
|
||||
t1.stop
|
||||
l.stop
|
||||
}
|
||||
|
||||
@Test def testSmallestMailboxFirstDispatcher = {
|
||||
val t1ProcessedCount = new AtomicInteger(0)
|
||||
val latch = new CountDownLatch(500)
|
||||
val t1 = actor {
|
||||
case x =>
|
||||
Thread.sleep(50) // slow actor
|
||||
t1ProcessedCount.incrementAndGet
|
||||
latch.countDown
|
||||
}
|
||||
|
||||
val t2ProcessedCount = new AtomicInteger(0)
|
||||
val t2 = actor {
|
||||
case x => t2ProcessedCount.incrementAndGet
|
||||
latch.countDown
|
||||
}
|
||||
val d = loadBalancerActor(new SmallestMailboxFirstIterator(t1 :: t2 :: Nil))
|
||||
for (i <- 1 to 500) d ! i
|
||||
val done = latch.await(10,TimeUnit.SECONDS)
|
||||
done must be (true)
|
||||
t1ProcessedCount.get must be < (t2ProcessedCount.get) // because t1 is much slower and thus has a bigger mailbox all the time
|
||||
for(a <- List(t1,t2,d)) a.stop
|
||||
}
|
||||
|
||||
@Test def testListener = {
|
||||
val latch = new CountDownLatch(2)
|
||||
val foreachListener = new CountDownLatch(2)
|
||||
val num = new AtomicInteger(0)
|
||||
val i = actorOf(new Actor with Listeners {
|
||||
def receive = listenerManagement orElse {
|
||||
case "foo" => gossip("bar")
|
||||
}
|
||||
})
|
||||
i.start
|
||||
|
||||
def newListener = actor {
|
||||
case "bar" =>
|
||||
num.incrementAndGet
|
||||
latch.countDown
|
||||
case "foo" => foreachListener.countDown
|
||||
}
|
||||
|
||||
val a1 = newListener
|
||||
val a2 = newListener
|
||||
val a3 = newListener
|
||||
|
||||
i ! Listen(a1)
|
||||
i ! Listen(a2)
|
||||
i ! Listen(a3)
|
||||
i ! Deafen(a3)
|
||||
i ! WithListeners(_ ! "foo")
|
||||
i ! "foo"
|
||||
|
||||
val done = latch.await(5,TimeUnit.SECONDS)
|
||||
done must be (true)
|
||||
num.get must be (2)
|
||||
val withListeners = foreachListener.await(5,TimeUnit.SECONDS)
|
||||
withListeners must be (true)
|
||||
for(a <- List(i,a1,a2,a3)) a.stop
|
||||
}
|
||||
|
||||
@Test def testIsDefinedAt = {
|
||||
import se.scalablesolutions.akka.actor.ActorRef
|
||||
|
||||
val (testMsg1,testMsg2,testMsg3,testMsg4) = ("test1","test2","test3","test4")
|
||||
|
||||
val t1 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg1` => self.reply(3)
|
||||
case `testMsg2` => self.reply(7)
|
||||
}
|
||||
} ).start
|
||||
|
||||
val t2 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg1` => self.reply(3)
|
||||
case `testMsg2` => self.reply(7)
|
||||
}
|
||||
} ).start
|
||||
|
||||
val t3 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg1` => self.reply(3)
|
||||
case `testMsg2` => self.reply(7)
|
||||
}
|
||||
} ).start
|
||||
|
||||
val t4 = actorOf( new Actor() {
|
||||
def receive = {
|
||||
case `testMsg1` => self.reply(3)
|
||||
case `testMsg2` => self.reply(7)
|
||||
}
|
||||
} ).start
|
||||
|
||||
val d1 = loadBalancerActor(new SmallestMailboxFirstIterator(t1 :: t2 :: Nil))
|
||||
val d2 = loadBalancerActor(new CyclicIterator[ActorRef](t3 :: t4 :: Nil))
|
||||
|
||||
t1.isDefinedAt(testMsg1) must be (true)
|
||||
t1.isDefinedAt(testMsg3) must be (false)
|
||||
t2.isDefinedAt(testMsg1) must be (true)
|
||||
t2.isDefinedAt(testMsg3) must be (false)
|
||||
d1.isDefinedAt(testMsg1) must be (true)
|
||||
d1.isDefinedAt(testMsg3) must be (false)
|
||||
d2.isDefinedAt(testMsg1) must be (true)
|
||||
d2.isDefinedAt(testMsg3) must be (false)
|
||||
|
||||
for(a <- List(t1,t2,d1,d2)) a.stop
|
||||
}
|
||||
}
|
||||
13
akka-actors/src/test/scala/ticket/Ticket001Spec.scala
Normal file
13
akka-actors/src/test/scala/ticket/Ticket001Spec.scala
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package se.scalablesolutions.akka.actor.ticket
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class Ticket001Spec extends WordSpec with MustMatchers {
|
||||
|
||||
"An XXX" should {
|
||||
"do YYY" in {
|
||||
1 must be (1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import java.io.File
|
||||
import java.net.{URL, URLClassLoader}
|
||||
import java.util.jar.JarFile
|
||||
import java.util.Enumeration
|
||||
|
||||
import se.scalablesolutions.akka.util.{Bootable, Logging}
|
||||
import se.scalablesolutions.akka.config.Config._
|
||||
|
||||
class AkkaDeployClassLoader(urls : List[URL], parent : ClassLoader) extends URLClassLoader(urls.toArray.asInstanceOf[Array[URL]],parent)
|
||||
{
|
||||
override def findResources(resource : String) = {
|
||||
val normalResult = super.findResources(resource)
|
||||
if(normalResult.hasMoreElements) normalResult else findDeployed(resource)
|
||||
}
|
||||
|
||||
def findDeployed(resource : String) = new Enumeration[URL]{
|
||||
private val it = getURLs.flatMap( listClassesInPackage(_,resource) ).iterator
|
||||
def hasMoreElements = it.hasNext
|
||||
def nextElement = it.next
|
||||
}
|
||||
|
||||
def listClassesInPackage(jar : URL, pkg : String) = {
|
||||
val f = new File(jar.getFile)
|
||||
val jf = new JarFile(f)
|
||||
try {
|
||||
val es = jf.entries
|
||||
var result = List[URL]()
|
||||
while(es.hasMoreElements)
|
||||
{
|
||||
val e = es.nextElement
|
||||
if(!e.isDirectory && e.getName.startsWith(pkg) && e.getName.endsWith(".class"))
|
||||
result ::= new URL("jar:" + f.toURI.toURL + "!/" + e)
|
||||
}
|
||||
result
|
||||
} finally {
|
||||
jf.close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all modules in the deploy directory (load and unload)
|
||||
*/
|
||||
trait BootableActorLoaderService extends Bootable with Logging {
|
||||
|
||||
val BOOT_CLASSES = config.getList("akka.boot")
|
||||
lazy val applicationLoader: Option[ClassLoader] = createApplicationClassLoader
|
||||
|
||||
protected def createApplicationClassLoader : Option[ClassLoader] = {
|
||||
Some(
|
||||
if (HOME.isDefined) {
|
||||
val CONFIG = HOME.get + "/config"
|
||||
val DEPLOY = HOME.get + "/deploy"
|
||||
val DEPLOY_DIR = new File(DEPLOY)
|
||||
if (!DEPLOY_DIR.exists) {
|
||||
log.error("Could not find a deploy directory at [%s]", DEPLOY)
|
||||
System.exit(-1)
|
||||
}
|
||||
val filesToDeploy = DEPLOY_DIR.listFiles.toArray.toList
|
||||
.asInstanceOf[List[File]].filter(_.getName.endsWith(".jar"))
|
||||
var dependencyJars: List[URL] = Nil
|
||||
filesToDeploy.map { file =>
|
||||
val jarFile = new JarFile(file)
|
||||
val en = jarFile.entries
|
||||
while (en.hasMoreElements) {
|
||||
val name = en.nextElement.getName
|
||||
if (name.endsWith(".jar")) dependencyJars ::= new File(
|
||||
String.format("jar:file:%s!/%s", jarFile.getName, name)).toURI.toURL
|
||||
}
|
||||
}
|
||||
val toDeploy = filesToDeploy.map(_.toURI.toURL)
|
||||
log.info("Deploying applications from [%s]: [%s]", DEPLOY, toDeploy)
|
||||
log.debug("Loading dependencies [%s]", dependencyJars)
|
||||
val allJars = toDeploy ::: dependencyJars
|
||||
|
||||
new AkkaDeployClassLoader(allJars,Thread.currentThread.getContextClassLoader)
|
||||
} else Thread.currentThread.getContextClassLoader)
|
||||
}
|
||||
|
||||
abstract override def onLoad = {
|
||||
applicationLoader.foreach(_ => log.info("Creating /deploy class-loader"))
|
||||
|
||||
super.onLoad
|
||||
|
||||
for (loader <- applicationLoader; clazz <- BOOT_CLASSES) {
|
||||
log.info("Loading boot class [%s]", clazz)
|
||||
loader.loadClass(clazz).newInstance
|
||||
}
|
||||
}
|
||||
|
||||
abstract override def onUnload = {
|
||||
super.onUnload
|
||||
ActorRegistry.shutdownAll
|
||||
}
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.stm.global._
|
||||
import se.scalablesolutions.akka.stm.TransactionManagement._
|
||||
import se.scalablesolutions.akka.stm.TransactionManagement
|
||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
||||
import se.scalablesolutions.akka.remote.{RemoteServer, RemoteRequestProtocolIdFactory, MessageSerializer}
|
||||
import se.scalablesolutions.akka.serialization.Serializer
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
|
||||
/**
|
||||
* Type class definition for Actor Serialization
|
||||
*/
|
||||
trait FromBinary[T <: Actor] {
|
||||
def fromBinary(bytes: Array[Byte], act: T): T
|
||||
}
|
||||
|
||||
trait ToBinary[T <: Actor] {
|
||||
def toBinary(t: T): Array[Byte]
|
||||
}
|
||||
|
||||
// client needs to implement Format[] for the respective actor
|
||||
trait Format[T <: Actor] extends FromBinary[T] with ToBinary[T]
|
||||
|
||||
/**
|
||||
* A default implementation for a stateless actor
|
||||
*
|
||||
* Create a Format object with the client actor as the implementation of the type class
|
||||
*
|
||||
* <pre>
|
||||
* object BinaryFormatMyStatelessActor {
|
||||
* implicit object MyStatelessActorFormat extends StatelessActorFormat[MyStatelessActor]
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
trait StatelessActorFormat[T <: Actor] extends Format[T] {
|
||||
def fromBinary(bytes: Array[Byte], act: T) = act
|
||||
def toBinary(ac: T) = Array.empty[Byte]
|
||||
}
|
||||
|
||||
/**
|
||||
* A default implementation of the type class for a Format that specifies a serializer
|
||||
*
|
||||
* Create a Format object with the client actor as the implementation of the type class and
|
||||
* a serializer object
|
||||
*
|
||||
* <pre>
|
||||
* object BinaryFormatMyJavaSerializableActor {
|
||||
* implicit object MyJavaSerializableActorFormat extends SerializerBasedActorFormat[MyJavaSerializableActor] {
|
||||
* val serializer = Serializer.Java
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
trait SerializerBasedActorFormat[T <: Actor] extends Format[T] {
|
||||
val serializer: Serializer
|
||||
def fromBinary(bytes: Array[Byte], act: T) = serializer.fromBinary(bytes, Some(act.self.actorClass)).asInstanceOf[T]
|
||||
def toBinary(ac: T) = serializer.toBinary(ac)
|
||||
}
|
||||
|
||||
/**
|
||||
* Module for local actor serialization
|
||||
*/
|
||||
object ActorSerialization {
|
||||
|
||||
def fromBinary[T <: Actor](bytes: Array[Byte])(implicit format: Format[T]): ActorRef =
|
||||
fromBinaryToLocalActorRef(bytes, format)
|
||||
|
||||
def toBinary[T <: Actor](a: ActorRef)(implicit format: Format[T]): Array[Byte] =
|
||||
toSerializedActorRefProtocol(a, format).toByteArray
|
||||
|
||||
// wrapper for implicits to be used by Java
|
||||
def fromBinaryJ[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef =
|
||||
fromBinary(bytes)(format)
|
||||
|
||||
// wrapper for implicits to be used by Java
|
||||
def toBinaryJ[T <: Actor](a: ActorRef, format: Format[T]): Array[Byte] =
|
||||
toBinary(a)(format)
|
||||
|
||||
private def toSerializedActorRefProtocol[T <: Actor](actorRef: ActorRef, format: Format[T]): SerializedActorRefProtocol = {
|
||||
val lifeCycleProtocol: Option[LifeCycleProtocol] = {
|
||||
def setScope(builder: LifeCycleProtocol.Builder, scope: Scope) = scope match {
|
||||
case Permanent => builder.setLifeCycle(LifeCycleType.PERMANENT)
|
||||
case Temporary => builder.setLifeCycle(LifeCycleType.TEMPORARY)
|
||||
}
|
||||
val builder = LifeCycleProtocol.newBuilder
|
||||
actorRef.lifeCycle match {
|
||||
case Some(LifeCycle(scope)) =>
|
||||
setScope(builder, scope)
|
||||
Some(builder.build)
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
||||
val originalAddress = AddressProtocol.newBuilder
|
||||
.setHostname(actorRef.homeAddress.getHostName)
|
||||
.setPort(actorRef.homeAddress.getPort)
|
||||
.build
|
||||
|
||||
val builder = SerializedActorRefProtocol.newBuilder
|
||||
.setUuid(actorRef.uuid)
|
||||
.setId(actorRef.id)
|
||||
.setActorClassname(actorRef.actorClass.getName)
|
||||
.setOriginalAddress(originalAddress)
|
||||
.setIsTransactor(actorRef.isTransactor)
|
||||
.setTimeout(actorRef.timeout)
|
||||
|
||||
actorRef.receiveTimeout.foreach(builder.setReceiveTimeout(_))
|
||||
builder.setActorInstance(ByteString.copyFrom(format.toBinary(actorRef.actor.asInstanceOf[T])))
|
||||
lifeCycleProtocol.foreach(builder.setLifeCycle(_))
|
||||
actorRef.supervisor.foreach(s => builder.setSupervisor(RemoteActorSerialization.toRemoteActorRefProtocol(s)))
|
||||
// FIXME: how to serialize the hotswap PartialFunction ??
|
||||
//hotswap.foreach(builder.setHotswapStack(_))
|
||||
builder.build
|
||||
}
|
||||
|
||||
private def fromBinaryToLocalActorRef[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef =
|
||||
fromProtobufToLocalActorRef(SerializedActorRefProtocol.newBuilder.mergeFrom(bytes).build, format, None)
|
||||
|
||||
private def fromProtobufToLocalActorRef[T <: Actor](
|
||||
protocol: SerializedActorRefProtocol, format: Format[T], loader: Option[ClassLoader]): ActorRef = {
|
||||
Actor.log.debug("Deserializing SerializedActorRefProtocol to LocalActorRef:\n" + protocol)
|
||||
|
||||
val serializer =
|
||||
if (format.isInstanceOf[SerializerBasedActorFormat[_]])
|
||||
Some(format.asInstanceOf[SerializerBasedActorFormat[_]].serializer)
|
||||
else None
|
||||
|
||||
val lifeCycle =
|
||||
if (protocol.hasLifeCycle) {
|
||||
val lifeCycleProtocol = protocol.getLifeCycle
|
||||
Some(if (lifeCycleProtocol.getLifeCycle == LifeCycleType.PERMANENT) LifeCycle(Permanent)
|
||||
else if (lifeCycleProtocol.getLifeCycle == LifeCycleType.TEMPORARY) LifeCycle(Temporary)
|
||||
else throw new IllegalActorStateException("LifeCycle type is not valid: " + lifeCycleProtocol.getLifeCycle))
|
||||
} else None
|
||||
|
||||
val supervisor =
|
||||
if (protocol.hasSupervisor)
|
||||
Some(RemoteActorSerialization.fromProtobufToRemoteActorRef(protocol.getSupervisor, loader))
|
||||
else None
|
||||
|
||||
val hotswap =
|
||||
if (serializer.isDefined && protocol.hasHotswapStack) Some(serializer.get
|
||||
.fromBinary(protocol.getHotswapStack.toByteArray, Some(classOf[PartialFunction[Any, Unit]]))
|
||||
.asInstanceOf[PartialFunction[Any, Unit]])
|
||||
else None
|
||||
|
||||
val ar = new LocalActorRef(
|
||||
protocol.getUuid,
|
||||
protocol.getId,
|
||||
protocol.getActorClassname,
|
||||
protocol.getActorInstance.toByteArray,
|
||||
protocol.getOriginalAddress.getHostname,
|
||||
protocol.getOriginalAddress.getPort,
|
||||
if (protocol.hasIsTransactor) protocol.getIsTransactor else false,
|
||||
if (protocol.hasTimeout) protocol.getTimeout else Actor.TIMEOUT,
|
||||
if (protocol.hasReceiveTimeout) Some(protocol.getReceiveTimeout) else None,
|
||||
lifeCycle,
|
||||
supervisor,
|
||||
hotswap,
|
||||
loader.getOrElse(getClass.getClassLoader), // TODO: should we fall back to getClass.getClassLoader?
|
||||
protocol.getMessagesList.toArray.toList.asInstanceOf[List[RemoteRequestProtocol]], format)
|
||||
|
||||
if (format.isInstanceOf[SerializerBasedActorFormat[_]] == false)
|
||||
format.fromBinary(protocol.getActorInstance.toByteArray, ar.actor.asInstanceOf[T])
|
||||
ar
|
||||
}
|
||||
}
|
||||
|
||||
object RemoteActorSerialization {
|
||||
/**
|
||||
* Deserializes a byte array (Array[Byte]) into an RemoteActorRef instance.
|
||||
*/
|
||||
def fromBinaryToRemoteActorRef(bytes: Array[Byte]): ActorRef =
|
||||
fromProtobufToRemoteActorRef(RemoteActorRefProtocol.newBuilder.mergeFrom(bytes).build, None)
|
||||
|
||||
/**
|
||||
* Deserializes a byte array (Array[Byte]) into an RemoteActorRef instance.
|
||||
*/
|
||||
def fromBinaryToRemoteActorRef(bytes: Array[Byte], loader: ClassLoader): ActorRef =
|
||||
fromProtobufToRemoteActorRef(RemoteActorRefProtocol.newBuilder.mergeFrom(bytes).build, Some(loader))
|
||||
|
||||
/**
|
||||
* Deserializes a RemoteActorRefProtocol Protocol Buffers (protobuf) Message into an RemoteActorRef instance.
|
||||
*/
|
||||
private[akka] def fromProtobufToRemoteActorRef(protocol: RemoteActorRefProtocol, loader: Option[ClassLoader]): ActorRef = {
|
||||
Actor.log.debug("Deserializing RemoteActorRefProtocol to RemoteActorRef:\n" + protocol)
|
||||
RemoteActorRef(
|
||||
protocol.getUuid,
|
||||
protocol.getActorClassname,
|
||||
protocol.getHomeAddress.getHostname,
|
||||
protocol.getHomeAddress.getPort,
|
||||
protocol.getTimeout,
|
||||
loader)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the ActorRef instance into a Protocol Buffers (protobuf) Message.
|
||||
*/
|
||||
def toRemoteActorRefProtocol(ar: ActorRef): RemoteActorRefProtocol = {
|
||||
import ar._
|
||||
val host = homeAddress.getHostName
|
||||
val port = homeAddress.getPort
|
||||
|
||||
if (!registeredInRemoteNodeDuringSerialization) {
|
||||
Actor.log.debug("Register serialized Actor [%s] as remote @ [%s:%s]", actorClass.getName, host, port)
|
||||
RemoteServer.getOrCreateServer(homeAddress)
|
||||
RemoteServer.registerActor(homeAddress, uuid, ar)
|
||||
registeredInRemoteNodeDuringSerialization = true
|
||||
}
|
||||
|
||||
RemoteActorRefProtocol.newBuilder
|
||||
.setUuid(uuid)
|
||||
.setActorClassname(actorClass.getName)
|
||||
.setHomeAddress(AddressProtocol.newBuilder.setHostname(host).setPort(port).build)
|
||||
.setTimeout(timeout)
|
||||
.build
|
||||
}
|
||||
|
||||
def createRemoteRequestProtocolBuilder(actorRef: ActorRef, message: Any, isOneWay: Boolean, senderOption: Option[ActorRef]):
|
||||
RemoteRequestProtocol.Builder = {
|
||||
import actorRef._
|
||||
|
||||
val actorInfo = ActorInfoProtocol.newBuilder
|
||||
.setUuid(uuid)
|
||||
.setTarget(actorClassName)
|
||||
.setTimeout(timeout)
|
||||
.setActorType(ActorType.SCALA_ACTOR)
|
||||
.build
|
||||
|
||||
val request = RemoteRequestProtocol.newBuilder
|
||||
.setId(RemoteRequestProtocolIdFactory.nextId)
|
||||
.setMessage(MessageSerializer.serialize(message))
|
||||
.setActorInfo(actorInfo)
|
||||
.setIsOneWay(isOneWay)
|
||||
|
||||
val id = registerSupervisorAsRemoteActor
|
||||
if (id.isDefined) request.setSupervisorUuid(id.get)
|
||||
|
||||
senderOption.foreach { sender =>
|
||||
RemoteServer.getOrCreateServer(sender.homeAddress).register(sender.uuid, sender)
|
||||
request.setSender(toRemoteActorRefProtocol(sender))
|
||||
}
|
||||
request
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ package se.scalablesolutions.akka.remote
|
|||
|
||||
import se.scalablesolutions.akka.serialization.{Serializer, Serializable}
|
||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
||||
import se.scalablesolutions.akka.util._
|
||||
|
||||
import com.google.protobuf.{Message, ByteString}
|
||||
import se.scalablesolutions.akka.util._
|
||||
|
||||
object MessageSerializer extends Logging {
|
||||
private var SERIALIZER_JAVA: Serializer.Java = Serializer.Java
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ object RemoteNode extends RemoteServer
|
|||
*/
|
||||
object RemoteServer {
|
||||
val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost")
|
||||
val PORT = config.getInt("akka.remote.server.port", 9999)
|
||||
val PORT = config.getInt("akka.remote.server.port", 9999)
|
||||
|
||||
val CONNECTION_TIMEOUT_MILLIS = Duration(config.getInt("akka.remote.server.connection-timeout", 1), TIME_UNIT)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ object Compression {
|
|||
*/
|
||||
object LZF {
|
||||
import voldemort.store.compress.lzf._
|
||||
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder.encode(bytes)
|
||||
def uncompress(bytes: Array[Byte]): Array[Byte] = LZFDecoder.decode(bytes)
|
||||
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder encode bytes
|
||||
def uncompress(bytes: Array[Byte]): Array[Byte] = LZFDecoder decode bytes
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import se.scalablesolutions.akka.serialization.Serializable
|
||||
import se.scalablesolutions.akka.actor.annotation.transactionrequired
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart
|
||||
import se.scalablesolutions.akka.actor.annotation.inittransactionalstate
|
||||
import se.scalablesolutions.akka.actor.annotation.oneway
|
||||
import se.scalablesolutions.akka.stm._
|
||||
|
||||
import com.google.inject.Inject
|
||||
|
||||
trait Bar {
|
||||
@oneway
|
||||
def bar(msg: String): String
|
||||
def getExt: Ext
|
||||
}
|
||||
|
||||
class BarImpl extends Bar {
|
||||
@Inject private var ext: Ext = _
|
||||
def getExt: Ext = ext
|
||||
def bar(msg: String) = msg
|
||||
}
|
||||
|
||||
trait Ext
|
||||
class ExtImpl extends Ext
|
||||
|
||||
class Foo extends Serializable.JavaJSON {
|
||||
@Inject
|
||||
private var bar: Bar = _
|
||||
def body = this
|
||||
def getBar = bar
|
||||
def foo(msg: String): String = msg + "_foo "
|
||||
def bar(msg: String): String = bar.bar(msg)
|
||||
def longRunning = {
|
||||
Thread.sleep(10000)
|
||||
"test"
|
||||
}
|
||||
def throwsException: String = {
|
||||
if (true) throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
"test"
|
||||
}
|
||||
}
|
||||
|
||||
@serializable class InMemFailer {
|
||||
def fail = throw new RuntimeException("Expected exception; to test fault-tolerance")
|
||||
}
|
||||
|
||||
@transactionrequired
|
||||
class InMemStateful {
|
||||
private lazy val mapState = TransactionalState.newMap[String, String]
|
||||
private lazy val vectorState = TransactionalState.newVector[String]
|
||||
private lazy val refState = TransactionalState.newRef[String]
|
||||
|
||||
def getMapState(key: String): String = mapState.get(key).get
|
||||
def getVectorState: String = vectorState.last
|
||||
def getRefState: String = refState.get.get
|
||||
def setMapState(key: String, msg: String): Unit = mapState.put(key, msg)
|
||||
def setVectorState(msg: String): Unit = vectorState.add(msg)
|
||||
def setRefState(msg: String): Unit = refState.swap(msg)
|
||||
def success(key: String, msg: String): Unit = {
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
}
|
||||
|
||||
def success(key: String, msg: String, nested: InMemStatefulNested): Unit = {
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
nested.success(key, msg)
|
||||
}
|
||||
|
||||
def failure(key: String, msg: String, failer: InMemFailer): String = {
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
failer.fail
|
||||
msg
|
||||
}
|
||||
|
||||
def failure(key: String, msg: String, nested: InMemStatefulNested, failer: InMemFailer): String = {
|
||||
mapState.put(key, msg)
|
||||
vectorState.add(msg)
|
||||
refState.swap(msg)
|
||||
nested.failure(key, msg, failer)
|
||||
msg
|
||||
}
|
||||
|
||||
def thisMethodHangs(key: String, msg: String, failer: InMemFailer) = setMapState(key, msg)
|
||||
|
||||
@prerestart def preRestart = println("################ PRE RESTART")
|
||||
@postrestart def postRestart = println("################ POST RESTART")
|
||||
}
|
||||
|
||||
@transactionrequired
|
||||
class InMemStatefulNested extends InMemStateful
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue