splitted up akka-core into three modules; akka-actors, akka-typed-actors, akka-core

This commit is contained in:
Jonas Bonér 2010-08-24 23:21:28 +02:00
parent b7b79484ba
commit c67b17a912
149 changed files with 11195 additions and 1399 deletions

View file

@ -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>

View file

@ -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());
}
}

View file

@ -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");
}
}

View file

@ -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
}
}

View file

@ -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");
}
}
*/

View file

@ -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);
}
}

View file

@ -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
// }
}

View file

@ -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();
}
}

View file

@ -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) {
}
}
}

View file

@ -7,7 +7,6 @@ package se.scalablesolutions.akka.actor
import se.scalablesolutions.akka.dispatch._
import se.scalablesolutions.akka.config.Config._
import se.scalablesolutions.akka.config.ScalaConfig._
import se.scalablesolutions.akka.serialization.Serializer
import se.scalablesolutions.akka.util.Helpers.{narrow, narrowSilently}
import se.scalablesolutions.akka.util.{Logging, Duration}
import se.scalablesolutions.akka.AkkaException
@ -502,3 +501,7 @@ private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) {
*/
def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption)
}
trait Proxyable {
def swapProxiedActor(newInstance: Actor)
}

View file

@ -5,18 +5,15 @@
package se.scalablesolutions.akka.actor
import se.scalablesolutions.akka.dispatch._
import se.scalablesolutions.akka.config.Config.config
import se.scalablesolutions.akka.config.Config._
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
import se.scalablesolutions.akka.config.ScalaConfig._
import se.scalablesolutions.akka.stm.global._
import se.scalablesolutions.akka.stm.TransactionManagement._
import se.scalablesolutions.akka.stm.{TransactionManagement, TransactionSetAbortedException}
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient, MessageSerializer, RemoteRequestProtocolIdFactory}
import se.scalablesolutions.akka.serialization.{Serializer, BinaryString}
import se.scalablesolutions.akka.util.{HashCode, Logging, UUID, ReentrantGuard}
import se.scalablesolutions.akka.remote.{RemoteClientModule, RemoteServerModule}
import se.scalablesolutions.akka.AkkaException
import RemoteActorSerialization._
import org.multiverse.api.ThreadLocalTransaction._
import org.multiverse.commitbarriers.CountDownCommitBarrier
@ -33,8 +30,6 @@ import java.lang.reflect.Field
import scala.reflect.BeanProperty
import com.google.protobuf.ByteString
/**
* ActorRef is an immutable and serializable handle to an Actor.
* <p/>
@ -67,15 +62,18 @@ import com.google.protobuf.ByteString
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
trait ActorRef extends ActorRefShared with TransactionManagement with java.lang.Comparable[ActorRef] {
scalaRef: ScalaActorRef =>
trait ActorRef extends
ActorRefShared with
TransactionManagement with
Logging with
java.lang.Comparable[ActorRef] { scalaRef: ScalaActorRef =>
// Only mutable for RemoteServer in order to maintain identity across nodes
@volatile protected[akka] var _uuid = UUID.newUuid.toString
@volatile protected[this] var _isRunning = false
@volatile protected[this] var _isShutDown = false
@volatile protected[akka] var _isBeingRestarted = false
@volatile protected[akka] var _homeAddress = new InetSocketAddress(RemoteServer.HOSTNAME, RemoteServer.PORT)
@volatile protected[akka] var _homeAddress = new InetSocketAddress(RemoteServerModule.HOSTNAME, RemoteServerModule.PORT)
@volatile protected[akka] var _futureTimeout: Option[ScheduledFuture[AnyRef]] = None
@volatile protected[akka] var startOnCreation = false
@volatile protected[akka] var registeredInRemoteNodeDuringSerialization = false
@ -686,8 +684,6 @@ class LocalActorRef private[akka](
// used only for deserialization
private[akka] def this(__uuid: String,
__id: String,
__actorClassName: String,
__actorBytes: Array[Byte],
__hostname: String,
__port: Int,
__isTransactor: Boolean,
@ -697,16 +693,8 @@ class LocalActorRef private[akka](
__supervisor: Option[ActorRef],
__hotswap: Option[PartialFunction[Any, Unit]],
__loader: ClassLoader,
__messages: List[RemoteRequestProtocol],
__format: Format[_ <: Actor]) = {
this(() => {
val actorClass = __loader.loadClass(__actorClassName)
if (__format.isInstanceOf[SerializerBasedActorFormat[_]])
__format.asInstanceOf[SerializerBasedActorFormat[_]]
.serializer
.fromBinary(__actorBytes, Some(actorClass)).asInstanceOf[Actor]
else actorClass.newInstance.asInstanceOf[Actor]
})
__factory: () => Actor) = {
this(__factory)
loader = Some(__loader)
isDeserialized = true
_uuid = __uuid
@ -721,7 +709,6 @@ class LocalActorRef private[akka](
actorSelfFields._1.set(actor, this)
actorSelfFields._2.set(actor, Some(this))
start
__messages.foreach(message => this ! MessageSerializer.deserialize(message.getMessage))
checkReceiveTimeout
ActorRegistry.register(this)
}
@ -755,19 +742,22 @@ class LocalActorRef private[akka](
/**
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
*/
def makeRemote(hostname: String, port: Int): Unit =
def makeRemote(hostname: String, port: Int): Unit = {
RemoteClientModule.ensureRemotingEnabled
if (!isRunning || isBeingRestarted) makeRemote(new InetSocketAddress(hostname, port))
else throw new ActorInitializationException(
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
}
/**
* Invoking 'makeRemote' means that an actor will be moved to and invoked on a remote host.
*/
def makeRemote(address: InetSocketAddress): Unit = guard.withGuard {
RemoteClientModule.ensureRemotingEnabled
if (!isRunning || isBeingRestarted) {
_remoteAddress = Some(address)
RemoteClient.register(address.getHostName, address.getPort, uuid)
homeAddress = (RemoteServer.HOSTNAME, RemoteServer.PORT)
RemoteClientModule.register(address, uuid)
homeAddress = (RemoteServerModule.HOSTNAME, RemoteServerModule.PORT)
} else throw new ActorInitializationException(
"Can't make a running actor remote. Make sure you call 'makeRemote' before 'start'.")
}
@ -839,9 +829,10 @@ class LocalActorRef private[akka](
_isShutDown = true
actor.shutdown
ActorRegistry.unregister(this)
remoteAddress.foreach(address => RemoteClient.unregister(
address.getHostName, address.getPort, uuid))
RemoteNode.unregister(this)
remoteAddress.foreach { address =>
RemoteClientModule.unregister(address, uuid)
}
RemoteClientModule.unregister(this)
nullOutActorRefReferencesFor(actorInstance.get)
} //else if (isBeingRestarted) throw new ActorKilledException("Actor [" + toString + "] is being restarted.")
}
@ -896,6 +887,7 @@ class LocalActorRef private[akka](
* To be invoked from within the actor itself.
*/
def startLinkRemote(actorRef: ActorRef, hostname: String, port: Int) = guard.withGuard {
RemoteClientModule.ensureRemotingEnabled
try {
actorRef.makeRemote(hostname, port)
actorRef.start
@ -921,6 +913,7 @@ class LocalActorRef private[akka](
* To be invoked from within the actor itself.
*/
def spawnRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
RemoteClientModule.ensureRemotingEnabled
val actor = spawnButDoNotStart(clazz)
actor.makeRemote(hostname, port)
actor.start
@ -948,6 +941,7 @@ class LocalActorRef private[akka](
* To be invoked from within the actor itself.
*/
def spawnLinkRemote(clazz: Class[_ <: Actor], hostname: String, port: Int): ActorRef = guard.withGuard {
RemoteClientModule.ensureRemotingEnabled
val actor = spawnButDoNotStart(clazz)
try {
actor.makeRemote(hostname, port)
@ -984,10 +978,8 @@ class LocalActorRef private[akka](
protected[akka] def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
joinTransaction(message)
if (remoteAddress.isDefined) {
RemoteClient.clientFor(remoteAddress.get).send[Any](
createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
} else {
if (remoteAddress.isDefined) RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
else {
val invocation = new MessageInvocation(this, message, senderOption, None, transactionSet.get)
invocation.send
}
@ -1000,12 +992,9 @@ class LocalActorRef private[akka](
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
joinTransaction(message)
if (remoteAddress.isDefined) {
val future = RemoteClient.clientFor(remoteAddress.get).send(
createRemoteRequestProtocolBuilder(this, message, false, senderOption).build, senderFuture)
if (future.isDefined) future.get
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
} else {
if (remoteAddress.isDefined) RemoteClientModule.send(
message, senderOption, senderFuture, remoteAddress.get, this)
else {
val future = if (senderFuture.isDefined) senderFuture.get
else new DefaultCompletableFuture[T](timeout)
val invocation = new MessageInvocation(
@ -1088,7 +1077,7 @@ class LocalActorRef private[akka](
Actor.log.debug("Restarting linked actors for actor [%s].", id)
restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id)
if (isTypedActorDispatcher(failedActor)) restartTypedActorDispatcher(failedActor, reason)
if (isProxyableDispatcher(failedActor)) restartProxyableDispatcher(failedActor, reason)
else restartActor(failedActor, reason)
_isBeingRestarted = false
}
@ -1107,8 +1096,9 @@ class LocalActorRef private[akka](
}
protected[akka] def registerSupervisorAsRemoteActor: Option[String] = guard.withGuard {
RemoteClientModule.ensureRemotingEnabled
if (_supervisor.isDefined) {
RemoteClient.clientFor(remoteAddress.get).registerSupervisorForActor(this)
remoteAddress.foreach(address => RemoteClientModule.registerSupervisorForActor(address, this))
Some(_supervisor.get.uuid)
} else None
}
@ -1126,9 +1116,9 @@ class LocalActorRef private[akka](
// ========= PRIVATE FUNCTIONS =========
private def isTypedActorDispatcher(a: Actor): Boolean = a.isInstanceOf[TypedActor]
private def isProxyableDispatcher(a: Actor): Boolean = a.isInstanceOf[Proxyable]
private def restartTypedActorDispatcher(failedActor: Actor, reason: Throwable) = {
private def restartProxyableDispatcher(failedActor: Actor, reason: Throwable) = {
failedActor.preRestart(reason)
failedActor.postRestart(reason)
}
@ -1140,7 +1130,8 @@ class LocalActorRef private[akka](
freshActor.init
freshActor.initTransactionalState
actorInstance.set(freshActor)
if (failedActor.isInstanceOf[TypedActor]) failedActor.asInstanceOf[TypedActor].swapInstanceInProxy(freshActor)
if (failedActor.isInstanceOf[Proxyable])
failedActor.asInstanceOf[Proxyable].swapProxiedActor(freshActor)
Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id)
freshActor.postRestart(reason)
}
@ -1316,6 +1307,7 @@ class LocalActorRef private[akka](
checkReceiveTimeout
}
/*
private def serializeMessage(message: AnyRef): AnyRef = if (Actor.SERIALIZE_MESSAGES) {
if (!message.isInstanceOf[String] &&
!message.isInstanceOf[Byte] &&
@ -1339,6 +1331,7 @@ class LocalActorRef private[akka](
Serializer.Java.deepClone(message)
} else message
} else message
*/
}
/**
@ -1347,7 +1340,7 @@ class LocalActorRef private[akka](
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object RemoteActorSystemMessage {
val Stop = BinaryString("RemoteActorRef:stop")
val Stop = "RemoteActorRef:stop".intern
}
/**
@ -1357,26 +1350,31 @@ object RemoteActorSystemMessage {
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
private[akka] case class RemoteActorRef private[akka] (
uuuid: String, val className: String, val hostname: String, val port: Int, _timeout: Long, loader: Option[ClassLoader])
// uuid: String, className: String, hostname: String, port: Int, timeOut: Long, isOnRemoteHost: Boolean) extends ActorRef {
uuuid: String,
val className: String,
val hostname: String,
val port: Int,
_timeout: Long,
loader: Option[ClassLoader])
extends ActorRef with ScalaActorRef {
RemoteClientModule.ensureRemotingEnabled
_uuid = uuuid
timeout = _timeout
start
lazy val remoteClient = RemoteClient.clientFor(hostname, port, loader)
lazy val remoteClient = RemoteClientModule.clientFor(hostname, port, loader)
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit = {
remoteClient.send[Any](createRemoteRequestProtocolBuilder(this, message, true, senderOption).build, None)
}
def postMessageToMailbox(message: Any, senderOption: Option[ActorRef]): Unit =
RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
def postMessageToMailboxAndCreateFutureResultWithTimeout[T](
message: Any,
timeout: Long,
senderOption: Option[ActorRef],
senderFuture: Option[CompletableFuture[T]]): CompletableFuture[T] = {
val future = remoteClient.send(createRemoteRequestProtocolBuilder(this, message, false, senderOption).build, senderFuture)
val future = RemoteClientModule.send(message, senderOption, None, remoteAddress.get, this)
if (future.isDefined) future.get
else throw new IllegalActorStateException("Expected a future from remote call to actor " + toString)
}
@ -1582,15 +1580,12 @@ trait ScalaActorRef extends ActorRefShared { ref: ActorRef =>
def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = {
if (isRunning) {
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
val isTypedActor = message.isInstanceOf[JoinPoint]
if (isTypedActor && TypedActor.isOneWay(message.asInstanceOf[JoinPoint])) {
future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None)
}
val isMessageJoinPoint = TypedActorModule.resolveFutureIfMessageIsJoinPoint(message, future)
try {
future.await
} catch {
case e: FutureTimeoutException =>
if (isTypedActor) throw e
if (isMessageJoinPoint) throw e
else None
}
if (future.exception.isDefined) throw future.exception.get

View file

@ -7,7 +7,7 @@ package se.scalablesolutions.akka.actor
import se.scalablesolutions.akka.config.ScalaConfig._
import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
import se.scalablesolutions.akka.util.Logging
import se.scalablesolutions.akka.remote.RemoteServer
import se.scalablesolutions.akka.remote.RemoteServerModule
import se.scalablesolutions.akka.AkkaException
import Actor._
@ -162,8 +162,10 @@ sealed class Supervisor private[akka] (
_childActors.put(className, actorRef :: currentActors)
actorRef.lifeCycle = Some(lifeCycle)
supervisor.link(actorRef)
remoteAddress.foreach(address => RemoteServer.registerActor(
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef))
remoteAddress.foreach { address =>
RemoteServerModule.registerActor(
new InetSocketAddress(address.hostname, address.port), actorRef.uuid, actorRef)
}
case supervisorConfig @ SupervisorConfig(_, _) => // recursive supervisor configuration
val childSupervisor = Supervisor(supervisorConfig)
supervisor.link(childSupervisor.supervisor)

View file

@ -4,19 +4,27 @@
package se.scalablesolutions.akka.config
import se.scalablesolutions.akka.util.Logging
import se.scalablesolutions.akka.AkkaException
import se.scalablesolutions.akka.util.Logging
import se.scalablesolutions.akka.actor.{ActorRef, IllegalActorStateException}
import se.scalablesolutions.akka.dispatch.CompletableFuture
import net.lag.configgy.{Config => CConfig, Configgy, ParseException}
import java.net.InetSocketAddress
import java.lang.reflect.Method
class ConfigurationException(message: String) extends AkkaException(message)
class ModuleNotAvailableException(message: String) extends AkkaException(message)
object ConfigLogger extends Logging
/**
* Loads up the configuration (from the akka.conf file).
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
object Config extends Logging {
object Config {
val VERSION = "1.0-SNAPSHOT"
// Set Multiverse options for max speed
@ -37,7 +45,7 @@ object Config extends Logging {
val configFile = System.getProperty("akka.config", "")
try {
Configgy.configure(configFile)
log.info("Config loaded from -Dakka.config=%s", configFile)
ConfigLogger.log.info("Config loaded from -Dakka.config=%s", configFile)
} catch {
case e: ParseException => throw new ConfigurationException(
"Config could not be loaded from -Dakka.config=" + configFile +
@ -47,7 +55,7 @@ object Config extends Logging {
} else if (getClass.getClassLoader.getResource("akka.conf") != null) {
try {
Configgy.configureFromResource("akka.conf", getClass.getClassLoader)
log.info("Config loaded from the application classpath.")
ConfigLogger.log.info("Config loaded from the application classpath.")
} catch {
case e: ParseException => throw new ConfigurationException(
"Can't load 'akka.conf' config file from application classpath," +
@ -58,7 +66,7 @@ object Config extends Logging {
try {
val configFile = HOME.get + "/config/akka.conf"
Configgy.configure(configFile)
log.info("AKKA_HOME is defined as [%s], config loaded from [%s].", HOME.get, configFile)
ConfigLogger.log.info("AKKA_HOME is defined as [%s], config loaded from [%s].", HOME.get, configFile)
} catch {
case e: ParseException => throw new ConfigurationException(
"AKKA_HOME is defined as [" + HOME.get + "] " +
@ -67,7 +75,7 @@ object Config extends Logging {
}
Configgy.config
} else {
log.warning(
ConfigLogger.log.warning(
"\nCan't load 'akka.conf'." +
"\nOne of the three ways of locating the 'akka.conf' file needs to be defined:" +
"\n\t1. Define the '-Dakka.config=...' system property option." +

View file

@ -106,7 +106,8 @@ object TransactionContainer extends Logging {
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class TransactionContainer private (val tm: Either[Option[UserTransaction], Option[TransactionManager]]) {
class TransactionContainer private (
val tm: Either[Option[UserTransaction], Option[TransactionManager]]) extends Logging {
def registerSynchronization(sync: Synchronization) = {
TransactionContainer.findSynchronizationRegistry match { // try to use SynchronizationRegistry in JNDI

View file

@ -9,6 +9,8 @@ import se.scalablesolutions.akka.util.Logging
import org.multiverse.api.{Transaction => MultiverseTransaction}
import org.multiverse.templates.TransactionalCallable
object GlobalStm extends Logging
/**
* Global transaction management, global in the context of multiple threads.
* Use this if you need to have one transaction span multiple threads (or Actors).
@ -23,12 +25,14 @@ import org.multiverse.templates.TransactionalCallable
* }
* </pre>
*/
class GlobalStm extends TransactionManagement with Logging {
class GlobalStm extends TransactionManagement {
val DefaultGlobalTransactionConfig = TransactionConfig()
val DefaultGlobalTransactionFactory = TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
val DefaultGlobalTransactionFactory = TransactionFactory(
DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T = atomic(factory)(body)
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultGlobalTransactionFactory): T =
atomic(factory)(body)
def atomic[T](factory: TransactionFactory)(body: => T): T = {
factory.boilerplate.execute(new TransactionalCallable[T]() {
@ -37,7 +41,8 @@ class GlobalStm extends TransactionManagement with Logging {
factory.addHooks
val result = body
val txSet = getTransactionSetInScope
log.trace("Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]")
GlobalStm.log.trace(
"Committing global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]")
try {
txSet.tryJoinCommit(
mtx,

View file

@ -9,6 +9,8 @@ import se.scalablesolutions.akka.util.Logging
import org.multiverse.api.{Transaction => MultiverseTransaction}
import org.multiverse.templates.TransactionalCallable
object LocalStm extends Logging
/**
* Local transaction management, local in the context of threads.
* Use this if you do <b>not</b> need to have one transaction span
@ -27,16 +29,18 @@ import org.multiverse.templates.TransactionalCallable
class LocalStm extends TransactionManagement with Logging {
val DefaultLocalTransactionConfig = TransactionConfig()
val DefaultLocalTransactionFactory = TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction")
val DefaultLocalTransactionFactory = TransactionFactory(
DefaultLocalTransactionConfig, "DefaultLocalTransaction")
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T = atomic(factory)(body)
def atomic[T](body: => T)(implicit factory: TransactionFactory = DefaultLocalTransactionFactory): T =
atomic(factory)(body)
def atomic[T](factory: TransactionFactory)(body: => T): T = {
factory.boilerplate.execute(new TransactionalCallable[T]() {
def call(mtx: MultiverseTransaction): T = {
factory.addHooks
val result = body
log.trace("Committing local transaction [" + mtx + "]")
LocalStm.log.trace("Committing local transaction [" + mtx + "]")
result
}
})

View 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>

View 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

View file

@ -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)
}
}

View 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
}
}

View 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
}

View 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))
}
}

View file

@ -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))
}
}

View file

@ -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)
}
}

View 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
}
}

View file

@ -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))
}
}

View file

@ -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))
}
}

View 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&#233;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))
}
}

View 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
}*/
}

View 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)
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}
}

View 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
}
}

View file

@ -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
}
}

View file

@ -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))
}
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}
*/

View 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)
}
}
}

View 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))
}
}

View 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
}
}

View 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)
}
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -6,9 +6,9 @@ package se.scalablesolutions.akka.remote
import se.scalablesolutions.akka.serialization.{Serializer, Serializable}
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
import se.scalablesolutions.akka.util._
import com.google.protobuf.{Message, ByteString}
import se.scalablesolutions.akka.util._
object MessageSerializer extends Logging {
private var SERIALIZER_JAVA: Serializer.Java = Serializer.Java

View file

@ -14,8 +14,8 @@ object Compression {
*/
object LZF {
import voldemort.store.compress.lzf._
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder.encode(bytes)
def uncompress(bytes: Array[Byte]): Array[Byte] = LZFDecoder.decode(bytes)
def compress(bytes: Array[Byte]): Array[Byte] = LZFEncoder encode bytes
def uncompress(bytes: Array[Byte]): Array[Byte] = LZFDecoder decode bytes
}
}

View file

@ -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