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.dispatch._
|
||||||
import se.scalablesolutions.akka.config.Config._
|
import se.scalablesolutions.akka.config.Config._
|
||||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
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.Helpers.{narrow, narrowSilently}
|
||||||
import se.scalablesolutions.akka.util.{Logging, Duration}
|
import se.scalablesolutions.akka.util.{Logging, Duration}
|
||||||
import se.scalablesolutions.akka.AkkaException
|
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)
|
def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait Proxyable {
|
||||||
|
def swapProxiedActor(newInstance: Actor)
|
||||||
|
}
|
||||||
|
|
@ -5,18 +5,15 @@
|
||||||
package se.scalablesolutions.akka.actor
|
package se.scalablesolutions.akka.actor
|
||||||
|
|
||||||
import se.scalablesolutions.akka.dispatch._
|
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.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
||||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||||
import se.scalablesolutions.akka.stm.global._
|
import se.scalablesolutions.akka.stm.global._
|
||||||
import se.scalablesolutions.akka.stm.TransactionManagement._
|
import se.scalablesolutions.akka.stm.TransactionManagement._
|
||||||
import se.scalablesolutions.akka.stm.{TransactionManagement, TransactionSetAbortedException}
|
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.util.{HashCode, Logging, UUID, ReentrantGuard}
|
||||||
|
import se.scalablesolutions.akka.remote.{RemoteClientModule, RemoteServerModule}
|
||||||
import se.scalablesolutions.akka.AkkaException
|
import se.scalablesolutions.akka.AkkaException
|
||||||
import RemoteActorSerialization._
|
|
||||||
|
|
||||||
import org.multiverse.api.ThreadLocalTransaction._
|
import org.multiverse.api.ThreadLocalTransaction._
|
||||||
import org.multiverse.commitbarriers.CountDownCommitBarrier
|
import org.multiverse.commitbarriers.CountDownCommitBarrier
|
||||||
|
|
@ -33,8 +30,6 @@ import java.lang.reflect.Field
|
||||||
|
|
||||||
import scala.reflect.BeanProperty
|
import scala.reflect.BeanProperty
|
||||||
|
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActorRef is an immutable and serializable handle to an Actor.
|
* ActorRef is an immutable and serializable handle to an Actor.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|
@ -67,20 +62,23 @@ import com.google.protobuf.ByteString
|
||||||
*
|
*
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
trait ActorRef extends ActorRefShared with TransactionManagement with java.lang.Comparable[ActorRef] {
|
trait ActorRef extends
|
||||||
scalaRef: ScalaActorRef =>
|
ActorRefShared with
|
||||||
|
TransactionManagement with
|
||||||
|
Logging with
|
||||||
|
java.lang.Comparable[ActorRef] { scalaRef: ScalaActorRef =>
|
||||||
|
|
||||||
// Only mutable for RemoteServer in order to maintain identity across nodes
|
// Only mutable for RemoteServer in order to maintain identity across nodes
|
||||||
@volatile protected[akka] var _uuid = UUID.newUuid.toString
|
@volatile protected[akka] var _uuid = UUID.newUuid.toString
|
||||||
@volatile protected[this] var _isRunning = false
|
@volatile protected[this] var _isRunning = false
|
||||||
@volatile protected[this] var _isShutDown = false
|
@volatile protected[this] var _isShutDown = false
|
||||||
@volatile protected[akka] var _isBeingRestarted = 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 _futureTimeout: Option[ScheduledFuture[AnyRef]] = None
|
||||||
@volatile protected[akka] var startOnCreation = false
|
@volatile protected[akka] var startOnCreation = false
|
||||||
@volatile protected[akka] var registeredInRemoteNodeDuringSerialization = false
|
@volatile protected[akka] var registeredInRemoteNodeDuringSerialization = false
|
||||||
protected[akka] val guard = new ReentrantGuard
|
protected[akka] val guard = new ReentrantGuard
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User overridable callback/setting.
|
* User overridable callback/setting.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|
@ -681,13 +679,11 @@ class LocalActorRef private[akka](
|
||||||
if (runActorInitialization && !isDeserialized) initializeActorInstance
|
if (runActorInitialization && !isDeserialized) initializeActorInstance
|
||||||
|
|
||||||
private[akka] def this(clazz: Class[_ <: Actor]) = this(Left(Some(clazz)))
|
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
|
// used only for deserialization
|
||||||
private[akka] def this(__uuid: String,
|
private[akka] def this(__uuid: String,
|
||||||
__id: String,
|
__id: String,
|
||||||
__actorClassName: String,
|
|
||||||
__actorBytes: Array[Byte],
|
|
||||||
__hostname: String,
|
__hostname: String,
|
||||||
__port: Int,
|
__port: Int,
|
||||||
__isTransactor: Boolean,
|
__isTransactor: Boolean,
|
||||||
|
|
@ -697,16 +693,8 @@ class LocalActorRef private[akka](
|
||||||
__supervisor: Option[ActorRef],
|
__supervisor: Option[ActorRef],
|
||||||
__hotswap: Option[PartialFunction[Any, Unit]],
|
__hotswap: Option[PartialFunction[Any, Unit]],
|
||||||
__loader: ClassLoader,
|
__loader: ClassLoader,
|
||||||
__messages: List[RemoteRequestProtocol],
|
__factory: () => Actor) = {
|
||||||
__format: Format[_ <: Actor]) = {
|
this(__factory)
|
||||||
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]
|
|
||||||
})
|
|
||||||
loader = Some(__loader)
|
loader = Some(__loader)
|
||||||
isDeserialized = true
|
isDeserialized = true
|
||||||
_uuid = __uuid
|
_uuid = __uuid
|
||||||
|
|
@ -721,7 +709,6 @@ class LocalActorRef private[akka](
|
||||||
actorSelfFields._1.set(actor, this)
|
actorSelfFields._1.set(actor, this)
|
||||||
actorSelfFields._2.set(actor, Some(this))
|
actorSelfFields._2.set(actor, Some(this))
|
||||||
start
|
start
|
||||||
__messages.foreach(message => this ! MessageSerializer.deserialize(message.getMessage))
|
|
||||||
checkReceiveTimeout
|
checkReceiveTimeout
|
||||||
ActorRegistry.register(this)
|
ActorRegistry.register(this)
|
||||||
}
|
}
|
||||||
|
|
@ -755,21 +742,24 @@ class LocalActorRef private[akka](
|
||||||
/**
|
/**
|
||||||
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
|
* 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))
|
if (!isRunning || isBeingRestarted) makeRemote(new InetSocketAddress(hostname, port))
|
||||||
else throw new ActorInitializationException(
|
else throw new ActorInitializationException(
|
||||||
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
|
"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.
|
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
|
||||||
*/
|
*/
|
||||||
def makeRemote(address: InetSocketAddress): Unit = guard.withGuard {
|
def makeRemote(address: InetSocketAddress): Unit = guard.withGuard {
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
if (!isRunning || isBeingRestarted) {
|
if (!isRunning || isBeingRestarted) {
|
||||||
_remoteAddress = Some(address)
|
_remoteAddress = Some(address)
|
||||||
RemoteClient.register(address.getHostName, address.getPort, uuid)
|
RemoteClientModule.register(address, uuid)
|
||||||
homeAddress = (RemoteServer.HOSTNAME, RemoteServer.PORT)
|
homeAddress = (RemoteServerModule.HOSTNAME, RemoteServerModule.PORT)
|
||||||
} else throw new ActorInitializationException(
|
} else throw new ActorInitializationException(
|
||||||
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
|
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -839,9 +829,10 @@ class LocalActorRef private[akka](
|
||||||
_isShutDown = true
|
_isShutDown = true
|
||||||
actor.shutdown
|
actor.shutdown
|
||||||
ActorRegistry.unregister(this)
|
ActorRegistry.unregister(this)
|
||||||
remoteAddress.foreach(address => RemoteClient.unregister(
|
remoteAddress.foreach { address =>
|
||||||
address.getHostName, address.getPort, uuid))
|
RemoteClientModule.unregister(address, uuid)
|
||||||
RemoteNode.unregister(this)
|
}
|
||||||
|
RemoteClientModule.unregister(this)
|
||||||
nullOutActorRefReferencesFor(actorInstance.get)
|
nullOutActorRefReferencesFor(actorInstance.get)
|
||||||
} //else if (isBeingRestarted) throw new ActorKilledException("Actor [" + toString + "] is being restarted.")
|
} //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.
|
* To be invoked from within the actor itself.
|
||||||
*/
|
*/
|
||||||
def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int) = guard.withGuard {
|
def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int) = guard.withGuard {
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
try {
|
try {
|
||||||
actorRef.makeRemote(hostname, port)
|
actorRef.makeRemote(hostname, port)
|
||||||
actorRef.start
|
actorRef.start
|
||||||
|
|
@ -921,6 +913,7 @@ class LocalActorRef private[akka](
|
||||||
* To be invoked from within the actor itself.
|
* To be invoked from within the actor itself.
|
||||||
*/
|
*/
|
||||||
def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
val actor = spawnButDoNotStart(clazz)
|
val actor = spawnButDoNotStart(clazz)
|
||||||
actor.makeRemote(hostname, port)
|
actor.makeRemote(hostname, port)
|
||||||
actor.start
|
actor.start
|
||||||
|
|
@ -948,6 +941,7 @@ class LocalActorRef private[akka](
|
||||||
* To be invoked from within the actor itself.
|
* To be invoked from within the actor itself.
|
||||||
*/
|
*/
|
||||||
def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
val actor = spawnButDoNotStart(clazz)
|
val actor = spawnButDoNotStart(clazz)
|
||||||
try {
|
try {
|
||||||
actor.makeRemote(hostname, port)
|
actor.makeRemote(hostname, port)
|
||||||
|
|
@ -984,10 +978,8 @@ class LocalActorRef private[akka](
|
||||||
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
|
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
|
||||||
joinTransaction(message)
|
joinTransaction(message)
|
||||||
|
|
||||||
if (remoteAddress.isDefined) {
|
if (remoteAddress.isDefined) RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
|
||||||
RemoteClient.clientFor(remoteAddress.get).send[Any](
|
else {
|
||||||
createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
|
|
||||||
} else {
|
|
||||||
val invocation = new MessageInvocation(this, message, senderOption, None, transactionSet.get)
|
val invocation = new MessageInvocation(this, message, senderOption, None, transactionSet.get)
|
||||||
invocation.send
|
invocation.send
|
||||||
}
|
}
|
||||||
|
|
@ -1000,12 +992,9 @@ class LocalActorRef private[akka](
|
||||||
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
|
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
|
||||||
joinTransaction(message)
|
joinTransaction(message)
|
||||||
|
|
||||||
if (remoteAddress.isDefined) {
|
if (remoteAddress.isDefined) RemoteClientModule.send(
|
||||||
val future = RemoteClient.clientFor(remoteAddress.get).send(
|
message, senderOption, senderFuture, remoteAddress.get, this)
|
||||||
createRemoteRequestProtocolBuilder(this, message, false, senderOption).build, senderFuture)
|
else {
|
||||||
if (future.isDefined) future.get
|
|
||||||
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
|
|
||||||
} else {
|
|
||||||
val future = if (senderFuture.isDefined) senderFuture.get
|
val future = if (senderFuture.isDefined) senderFuture.get
|
||||||
else new DefaultCompletableFuture[T](timeout)
|
else new DefaultCompletableFuture[T](timeout)
|
||||||
val invocation = new MessageInvocation(
|
val invocation = new MessageInvocation(
|
||||||
|
|
@ -1088,7 +1077,7 @@ class LocalActorRef private[akka](
|
||||||
Actor.log.debug("Restarting linked actors for actor [%s].", id)
|
Actor.log.debug("Restarting linked actors for actor [%s].", id)
|
||||||
restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
|
restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
|
||||||
Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id)
|
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)
|
else restartActor(failedActor, reason)
|
||||||
_isBeingRestarted = false
|
_isBeingRestarted = false
|
||||||
}
|
}
|
||||||
|
|
@ -1107,8 +1096,9 @@ class LocalActorRef private[akka](
|
||||||
}
|
}
|
||||||
|
|
||||||
protected[akka] def registerSupervisorAsRemoteActor: Option[String] = guard.withGuard {
|
protected[akka] def registerSupervisorAsRemoteActor: Option[String] = guard.withGuard {
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
if (_supervisor.isDefined) {
|
if (_supervisor.isDefined) {
|
||||||
RemoteClient.clientFor(remoteAddress.get).registerSupervisorForActor(this)
|
remoteAddress.foreach(address => RemoteClientModule.registerSupervisorForActor(address, this))
|
||||||
Some(_supervisor.get.uuid)
|
Some(_supervisor.get.uuid)
|
||||||
} else None
|
} else None
|
||||||
}
|
}
|
||||||
|
|
@ -1126,9 +1116,9 @@ class LocalActorRef private[akka](
|
||||||
|
|
||||||
// ========= PRIVATE FUNCTIONS =========
|
// ========= 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.preRestart(reason)
|
||||||
failedActor.postRestart(reason)
|
failedActor.postRestart(reason)
|
||||||
}
|
}
|
||||||
|
|
@ -1140,7 +1130,8 @@ class LocalActorRef private[akka](
|
||||||
freshActor.init
|
freshActor.init
|
||||||
freshActor.initTransactionalState
|
freshActor.initTransactionalState
|
||||||
actorInstance.set(freshActor)
|
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)
|
Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id)
|
||||||
freshActor.postRestart(reason)
|
freshActor.postRestart(reason)
|
||||||
}
|
}
|
||||||
|
|
@ -1316,6 +1307,7 @@ class LocalActorRef private[akka](
|
||||||
checkReceiveTimeout
|
checkReceiveTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
private def serializeMessage(message: AnyRef): AnyRef = if (Actor.SERIALIZE_MESSAGES) {
|
private def serializeMessage(message: AnyRef): AnyRef = if (Actor.SERIALIZE_MESSAGES) {
|
||||||
if (!message.isInstanceOf[String] &&
|
if (!message.isInstanceOf[String] &&
|
||||||
!message.isInstanceOf[Byte] &&
|
!message.isInstanceOf[Byte] &&
|
||||||
|
|
@ -1339,6 +1331,7 @@ class LocalActorRef private[akka](
|
||||||
Serializer.Java.deepClone(message)
|
Serializer.Java.deepClone(message)
|
||||||
} else message
|
} else message
|
||||||
} else message
|
} else message
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1347,7 +1340,7 @@ class LocalActorRef private[akka](
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
object RemoteActorSystemMessage {
|
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>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
private[akka] case class RemoteActorRef private[akka] (
|
private[akka] case class RemoteActorRef private[akka] (
|
||||||
uuuid: String, val className: String, val hostname: String, val port: Int, _timeout: Long, loader: Option[ClassLoader])
|
uuuid: String,
|
||||||
// uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef {
|
val className: String,
|
||||||
|
val hostname: String,
|
||||||
|
val port: Int,
|
||||||
|
_timeout: Long,
|
||||||
|
loader: Option[ClassLoader])
|
||||||
extends ActorRef with ScalaActorRef {
|
extends ActorRef with ScalaActorRef {
|
||||||
|
|
||||||
|
RemoteClientModule.ensureRemotingEnabled
|
||||||
|
|
||||||
_uuid = uuuid
|
_uuid = uuuid
|
||||||
timeout = _timeout
|
timeout = _timeout
|
||||||
|
|
||||||
start
|
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 = {
|
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit =
|
||||||
remoteClient.send[Any](createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
|
RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
|
||||||
}
|
|
||||||
|
|
||||||
def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
|
||||||
message: Any,
|
message: Any,
|
||||||
timeout: Long,
|
timeout: Long,
|
||||||
senderOption: Option[ActorRef],
|
senderOption: Option[ActorRef],
|
||||||
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
|
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
|
if (future.isDefined) future.get
|
||||||
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
|
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] = {
|
def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = {
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
|
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
|
||||||
val isTypedActor = message.isInstanceOf[JoinPoint]
|
val isMessageJoinPoint = TypedActorModule.resolveFutureIfMessageIsJoinPoint(message, future)
|
||||||
if (isTypedActor && TypedActor.isOneWay(message.asInstanceOf[JoinPoint])) {
|
|
||||||
future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
future.await
|
future.await
|
||||||
} catch {
|
} catch {
|
||||||
case e: FutureTimeoutException =>
|
case e: FutureTimeoutException =>
|
||||||
if (isTypedActor) throw e
|
if (isMessageJoinPoint) throw e
|
||||||
else None
|
else None
|
||||||
}
|
}
|
||||||
if (future.exception.isDefined) throw future.exception.get
|
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.ScalaConfig._
|
||||||
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
|
||||||
import se.scalablesolutions.akka.util.Logging
|
import se.scalablesolutions.akka.util.Logging
|
||||||
import se.scalablesolutions.akka.remote.RemoteServer
|
import se.scalablesolutions.akka.remote.RemoteServerModule
|
||||||
import se.scalablesolutions.akka.AkkaException
|
import se.scalablesolutions.akka.AkkaException
|
||||||
import Actor._
|
import Actor._
|
||||||
|
|
||||||
|
|
@ -162,8 +162,10 @@ sealed class Supervisor private[akka] (
|
||||||
_childActors.put(className, actorRef :: currentActors)
|
_childActors.put(className, actorRef :: currentActors)
|
||||||
actorRef.lifeCycle = Some(lifeCycle)
|
actorRef.lifeCycle = Some(lifeCycle)
|
||||||
supervisor.link(actorRef)
|
supervisor.link(actorRef)
|
||||||
remoteAddress.foreach(address => RemoteServer.registerActor(
|
remoteAddress.foreach { address =>
|
||||||
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef))
|
RemoteServerModule.registerActor(
|
||||||
|
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef)
|
||||||
|
}
|
||||||
case supervisorConfig @ SupervisorConfig(_, _) => // recursive supervisor configuration
|
case supervisorConfig @ SupervisorConfig(_, _) => // recursive supervisor configuration
|
||||||
val childSupervisor = Supervisor(supervisorConfig)
|
val childSupervisor = Supervisor(supervisorConfig)
|
||||||
supervisor.link(childSupervisor.supervisor)
|
supervisor.link(childSupervisor.supervisor)
|
||||||
|
|
@ -4,19 +4,27 @@
|
||||||
|
|
||||||
package se.scalablesolutions.akka.config
|
package se.scalablesolutions.akka.config
|
||||||
|
|
||||||
import se.scalablesolutions.akka.util.Logging
|
|
||||||
import se.scalablesolutions.akka.AkkaException
|
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 net.lag.configgy.{Config => CConfig, Configgy, ParseException}
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
class ConfigurationException(message: String) extends AkkaException(message)
|
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).
|
* Loads up the configuration (from the akka.conf file).
|
||||||
*
|
*
|
||||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||||
*/
|
*/
|
||||||
object Config extends Logging {
|
object Config {
|
||||||
val VERSION = "1.0-SNAPSHOT"
|
val VERSION = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
// Set Multiverse options for max speed
|
// Set Multiverse options for max speed
|
||||||
|
|
@ -37,7 +45,7 @@ object Config extends Logging {
|
||||||
val configFile = System.getProperty("akka.config", "")
|
val configFile = System.getProperty("akka.config", "")
|
||||||
try {
|
try {
|
||||||
Configgy.configure(configFile)
|
Configgy.configure(configFile)
|
||||||
log.info("Config loaded from -Dakka.config=%s", configFile)
|
ConfigLogger.log.info("Config loaded from -Dakka.config=%s", configFile)
|
||||||
} catch {
|
} catch {
|
||||||
case e: ParseException => throw new ConfigurationException(
|
case e: ParseException => throw new ConfigurationException(
|
||||||
"Config could not be loaded from -Dakka.config=" + configFile +
|
"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) {
|
} else if (getClass.getClassLoader.getResource("akka.conf") != null) {
|
||||||
try {
|
try {
|
||||||
Configgy.configureFromResource("akka.conf", getClass.getClassLoader)
|
Configgy.configureFromResource("akka.conf", getClass.getClassLoader)
|
||||||
log.info("Config loaded from the application classpath.")
|
ConfigLogger.log.info("Config loaded from the application classpath.")
|
||||||
} catch {
|
} catch {
|
||||||
case e: ParseException => throw new ConfigurationException(
|
case e: ParseException => throw new ConfigurationException(
|
||||||
"Can't load 'akka.conf' config file from application classpath," +
|
"Can't load 'akka.conf' config file from application classpath," +
|
||||||
|
|
@ -58,7 +66,7 @@ object Config extends Logging {
|
||||||
try {
|
try {
|
||||||
val configFile = HOME.get + "/config/akka.conf"
|
val configFile = HOME.get + "/config/akka.conf"
|
||||||
Configgy.configure(configFile)
|
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 {
|
} catch {
|
||||||
case e: ParseException => throw new ConfigurationException(
|
case e: ParseException => throw new ConfigurationException(
|
||||||
"AKKA_HOME is defined as [" + HOME.get + "] " +
|
"AKKA_HOME is defined as [" + HOME.get + "] " +
|
||||||
|
|
@ -67,7 +75,7 @@ object Config extends Logging {
|
||||||
}
|
}
|
||||||
Configgy.config
|
Configgy.config
|
||||||
} else {
|
} else {
|
||||||
log.warning(
|
ConfigLogger.log.warning(
|
||||||
"\nCan't load 'akka.conf'." +
|
"\nCan't load 'akka.conf'." +
|
||||||
"\nOne of the three ways of locating the 'akka.conf' file needs to be defined:" +
|
"\nOne of the three ways of locating the 'akka.conf' file needs to be defined:" +
|
||||||
"\n\t1. Define the '-Dakka.config=...' system property option." +
|
"\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>
|
* @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) = {
|
def registerSynchronization(sync: Synchronization) = {
|
||||||
TransactionContainer.findSynchronizationRegistry match { // try to use SynchronizationRegistry in JNDI
|
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.api.{Transaction => MultiverseTransaction}
|
||||||
import org.multiverse.templates.TransactionalCallable
|
import org.multiverse.templates.TransactionalCallable
|
||||||
|
|
||||||
|
object GlobalStm extends Logging
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global transaction management, global in the context of multiple threads.
|
* Global transaction management, global in the context of multiple threads.
|
||||||
* Use this if you need to have one transaction span multiple threads (or Actors).
|
* Use this if you need to have one transaction span multiple threads (or Actors).
|
||||||
|
|
@ -23,12 +25,14 @@ import org.multiverse.templates.TransactionalCallable
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
class GlobalStm extends TransactionManagement with Logging {
|
class GlobalStm extends TransactionManagement {
|
||||||
|
|
||||||
val DefaultGlobalTransactionConfig = TransactionConfig()
|
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 = {
|
def atomic[T](factory: TransactionFactory)(body: => T): T = {
|
||||||
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
||||||
|
|
@ -37,7 +41,8 @@ class GlobalStm extends TransactionManagement with Logging {
|
||||||
factory.addHooks
|
factory.addHooks
|
||||||
val result = body
|
val result = body
|
||||||
val txSet = getTransactionSetInScope
|
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 {
|
try {
|
||||||
txSet.tryJoinCommit(
|
txSet.tryJoinCommit(
|
||||||
mtx,
|
mtx,
|
||||||
|
|
@ -9,6 +9,8 @@ import se.scalablesolutions.akka.util.Logging
|
||||||
import org.multiverse.api.{Transaction => MultiverseTransaction}
|
import org.multiverse.api.{Transaction => MultiverseTransaction}
|
||||||
import org.multiverse.templates.TransactionalCallable
|
import org.multiverse.templates.TransactionalCallable
|
||||||
|
|
||||||
|
object LocalStm extends Logging
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local transaction management, local in the context of threads.
|
* Local transaction management, local in the context of threads.
|
||||||
* Use this if you do <b>not</b> need to have one transaction span
|
* 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 {
|
class LocalStm extends TransactionManagement with Logging {
|
||||||
|
|
||||||
val DefaultLocalTransactionConfig = TransactionConfig()
|
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 = {
|
def atomic[T](factory: TransactionFactory)(body: => T): T = {
|
||||||
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
factory.boilerplate.execute(new TransactionalCallable[T]() {
|
||||||
def call(mtx: MultiverseTransaction): T = {
|
def call(mtx: MultiverseTransaction): T = {
|
||||||
factory.addHooks
|
factory.addHooks
|
||||||
val result = body
|
val result = body
|
||||||
log.trace("Committing local transaction [" + mtx + "]")
|
LocalStm.log.trace("Committing local transaction [" + mtx + "]")
|
||||||
result
|
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.serialization.{Serializer, Serializable}
|
||||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
||||||
|
import se.scalablesolutions.akka.util._
|
||||||
|
|
||||||
import com.google.protobuf.{Message, ByteString}
|
import com.google.protobuf.{Message, ByteString}
|
||||||
import se.scalablesolutions.akka.util._
|
|
||||||
|
|
||||||
object MessageSerializer extends Logging {
|
object MessageSerializer extends Logging {
|
||||||
private var SERIALIZER_JAVA: Serializer.Java = Serializer.Java
|
private var SERIALIZER_JAVA: Serializer.Java = Serializer.Java
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ object RemoteNode extends RemoteServer
|
||||||
*/
|
*/
|
||||||
object RemoteServer {
|
object RemoteServer {
|
||||||
val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost")
|
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)
|
val CONNECTION_TIMEOUT_MILLIS = Duration(config.getInt("akka.remote.server.connection-timeout", 1), TIME_UNIT)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ object Compression {
|
||||||
*/
|
*/
|
||||||
object LZF {
|
object LZF {
|
||||||
import voldemort.store.compress.lzf._
|
import voldemort.store.compress.lzf._
|
||||||
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder.encode(bytes)
|
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder encode bytes
|
||||||
def uncompress(bytes: Array[Byte]): Array[Byte] = LZFDecoder.decode(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