diff --git a/akka-actors/src/main/scala/actor/ActiveObject.scala b/akka-actors/src/main/scala/actor/ActiveObject.scala index 7f2d2f8fa3..6ef7f4bd3f 100644 --- a/akka-actors/src/main/scala/actor/ActiveObject.scala +++ b/akka-actors/src/main/scala/actor/ActiveObject.scala @@ -25,11 +25,12 @@ class ActiveObjectInvocationTimeoutException(msg: String) extends ActiveObjectEx object Annotations { import se.scalablesolutions.akka.annotation._ - val oneway = classOf[oneway] - val transactionrequired = classOf[transactionrequired] - val prerestart = classOf[prerestart] - val postrestart = classOf[postrestart] - val immutable = classOf[immutable] + val oneway = classOf[oneway] + val transactionrequired = classOf[transactionrequired] + val prerestart = classOf[prerestart] + val postrestart = classOf[postrestart] + val immutable = classOf[immutable] + val inittransactionalstate = classOf[inittransactionalstate] } /** @@ -397,6 +398,7 @@ private[akka] class Dispatcher(val callbacks: Option[RestartCallbacks]) extends private[actor] var target: Option[AnyRef] = None private var preRestart: Option[Method] = None private var postRestart: Option[Method] = None + private var initTxState: Option[Method] = None private[actor] def initialize(targetClass: Class[_], targetInstance: AnyRef) = { if (targetClass.isAnnotationPresent(Annotations.transactionrequired)) makeTransactionRequired @@ -417,8 +419,8 @@ private[akka] class Dispatcher(val callbacks: Option[RestartCallbacks]) extends } // See if we have any annotation defined restart callbacks - if (!preRestart.isDefined) preRestart = methods.find( m => m.isAnnotationPresent(Annotations.prerestart)) - if (!postRestart.isDefined) postRestart = methods.find( m => m.isAnnotationPresent(Annotations.postrestart)) + if (!preRestart.isDefined) preRestart = methods.find(m => m.isAnnotationPresent(Annotations.prerestart)) + if (!postRestart.isDefined) postRestart = methods.find(m => m.isAnnotationPresent(Annotations.postrestart)) if (preRestart.isDefined && preRestart.get.getParameterTypes.length != 0) throw new IllegalStateException("Method annotated with @prerestart or defined as a restart callback in [" + targetClass.getName + "] must have a zero argument definition") @@ -427,6 +429,11 @@ private[akka] class Dispatcher(val callbacks: Option[RestartCallbacks]) extends if (preRestart.isDefined) preRestart.get.setAccessible(true) if (postRestart.isDefined) postRestart.get.setAccessible(true) + + // see if we have a method annotated with @inittransactionalstate, if so invoke it + initTxState = methods.find(m => m.isAnnotationPresent(Annotations.inittransactionalstate)) + if (initTxState.isDefined && initTxState.get.getParameterTypes.length != 0) throw new IllegalStateException("Method annotated with @inittransactionalstate must have a zero argument definition") + if (initTxState.isDefined) initTxState.get.setAccessible(true) } override def receive: PartialFunction[Any, Unit] = { @@ -450,6 +457,13 @@ private[akka] class Dispatcher(val callbacks: Option[RestartCallbacks]) extends } catch { case e: InvocationTargetException => throw e.getCause } } + override protected def initTransactionalState() { + try { + if (initTxState.isDefined && target.isDefined) initTxState.get.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*) + } catch { case e: InvocationTargetException => throw e.getCause } + } + + private def serializeArguments(joinpoint: JoinPoint) = { val args = joinpoint.getRtti.asInstanceOf[MethodRtti].getParameterValues var unserializable = false diff --git a/akka-actors/src/main/scala/actor/Actor.scala b/akka-actors/src/main/scala/actor/Actor.scala index 015fb02974..c20884d5ca 100644 --- a/akka-actors/src/main/scala/actor/Actor.scala +++ b/akka-actors/src/main/scala/actor/Actor.scala @@ -63,9 +63,11 @@ trait Actor extends Logging with TransactionManagement { private var hotswap: Option[PartialFunction[Any, Unit]] = None private var config: Option[AnyRef] = None + @volatile protected[this] var isTransactional = false @volatile protected[this] var remoteAddress: Option[InetSocketAddress] = None @volatile protected[akka] var supervisor: Option[Actor] = None + protected[Actor] var mailbox: MessageQueue = _ protected[this] var senderFuture: Option[CompletableFutureResult] = None protected[this] val linkedActors = new CopyOnWriteArraySet[Actor] @@ -169,15 +171,6 @@ trait Actor extends Logging with TransactionManagement { */ protected def init(config: AnyRef) = {} - /** - * User overridable callback/setting. - * - * Optional callback method that is called during initialization. - * Used to initialize transactional state. - * To be implemented by subclassing actor. - */ - protected def initializeTransactionalState = {} - /** * User overridable callback/setting. * @@ -194,6 +187,14 @@ trait Actor extends Logging with TransactionManagement { */ protected def postRestart(reason: AnyRef, config: Option[AnyRef]) = {} + /** + * User overridable callback/setting. + * + * Optional callback method that is called during termination. + * To be implemented by subclassing actor. + */ + protected def initTransactionalState() = {} + /** * User overridable callback/setting. * @@ -552,10 +553,10 @@ trait Actor extends Logging with TransactionManagement { private val lifeCycle: PartialFunction[Any, Unit] = { case Init(config) => init(config) - case TransactionalInit => initializeTransactionalState case HotSwap(code) => hotswap = code case Restart(reason) => restart(reason) case Exit(dead, reason) => handleTrapExit(dead, reason) + case TransactionalInit => initTransactionalState } private[this] def handleTrapExit(dead: Actor, reason: Throwable): Unit = { diff --git a/akka-actors/src/test/scala/CamelSpec.scala b/akka-actors/src/test/scala/CamelSpec.scala deleted file mode 100644 index 7f9a546613..0000000000 --- a/akka-actors/src/test/scala/CamelSpec.scala +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (C) 2009 Scalable Solutions. - */ - -package se.scalablesolutions.akka.camel - -/* -import config.ActiveObjectGuiceConfigurator -import annotation.oneway -import config.ScalaConfig._ - -import com.google.inject.{AbstractModule, Scopes} -//import com.jteigen.scalatest.JUnit4Runner - -import org.apache.camel.component.bean.ProxyHelper -import org.junit.runner.RunWith -import org.scalatest._ -import org.scalatest.matchers._ - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.camel.CamelContext -import org.apache.camel.Endpoint -import org.apache.camel.Exchange -import org.apache.camel.Processor -import org.apache.camel.Producer -import org.apache.camel.builder.RouteBuilder -import org.apache.camel.impl.DefaultCamelContext - -// REQUIRES: -Djava.naming.factory.initial=org.apache.camel.util.jndi.CamelInitialContextFactory -*/ -/** - * @author Jonas Bonér - * -//@RunWith(classOf[JUnit4Runner]) -class CamelSpec extends Spec with ShouldMatchers { - - describe("A Camel routing scheme") { - it("should route message from direct:test to actor A using @Bean endpoint") { - - val latch = new CountDownLatch(1); - - val conf = new ActiveObjectGuiceConfigurator - conf.configure( - RestartStrategy(AllForOne, 3, 5000), - Component( - "camelfoo", - classOf[CamelFoo], - classOf[CamelFooImpl], - LifeCycle(Permanent, 1000), - 1000) :: - Nil - ).addRoutes(new RouteBuilder() { - def configure = { - from("direct:test").to("bean:camelfoo").process(new Processor() { - def process(e: Exchange) = { - println("Received exchange: " + e.getIn()) - latch.countDown - } - }) - }} - ).supervise - - val endpoint = conf.getRoutingEndpoint("direct:test") - val proxy = ProxyHelper.createProxy(endpoint, classOf[CamelFoo]) - - proxy.foo("hello there") - - val exchange = endpoint.createExchange - println("----- " + exchange) - - exchange.getIn().setBody("hello there") - - val producer = endpoint.createProducer - println("----- " + producer) - - producer.process(exchange) - - // now lets sleep for a while - val received = latch.await(5, TimeUnit.SECONDS) - received should equal (true) - conf.stop - } - } -} - -trait CamelFoo { - @oneway def foo(msg: String) -} -trait CamelBar { - def bar(msg: String): String -} - -class CamelFooImpl extends CamelFoo { - def foo(msg: String) = println("CamelFoo.foo:" + msg) -} -class CamelBarImpl extends CamelBar { - def bar(msg: String) = msg + "return_bar " -} - */ diff --git a/akka-actors/src/test/scala/InMemoryActorSpec.scala b/akka-actors/src/test/scala/InMemoryActorSpec.scala index 91147c5acc..96548f4621 100644 --- a/akka-actors/src/test/scala/InMemoryActorSpec.scala +++ b/akka-actors/src/test/scala/InMemoryActorSpec.scala @@ -27,15 +27,9 @@ class InMemStatefulActor extends Actor { timeout = 100000 makeTransactionRequired //dispatcher = se.scalablesolutions.akka.reactor.Dispatchers.newThreadBasedDispatcher(this) - private var mapState: TransactionalMap[String, String] = _ - private var vectorState: TransactionalVector[String] = _ - private var refState: TransactionalRef[String] = _ - - override def initializeTransactionalState = { - mapState = TransactionalState.newMap[String, String] - vectorState = TransactionalState.newVector[String] - refState = TransactionalState.newRef[String] - } + private lazy val mapState: TransactionalMap[String, String] = TransactionalState.newMap[String, String] + private lazy val vectorState: TransactionalVector[String] = TransactionalState.newVector[String] + private lazy val refState: TransactionalRef[String] = TransactionalState.newRef[String] def receive: PartialFunction[Any, Unit] = { case GetMapState(key) => diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/AllTest.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/AllTest.java index 4fbfd95031..b6753d3580 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/AllTest.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/AllTest.java @@ -9,11 +9,11 @@ public class AllTest extends TestCase { TestSuite suite = new TestSuite("All Java tests"); suite.addTestSuite(InMemoryStateTest.class); suite.addTestSuite(InMemNestedStateTest.class); + suite.addTestSuite(RemoteInMemoryStateTest.class); + suite.addTestSuite(ActiveObjectGuiceConfiguratorTest.class); //suite.addTestSuite(PersistentStateTest.class); //suite.addTestSuite(PersistentNestedStateTest.class); - suite.addTestSuite(RemoteInMemoryStateTest.class); //suite.addTestSuite(RemotePersistentStateTest.class); - suite.addTestSuite(ActiveObjectGuiceConfiguratorTest.class); //suite.addTestSuite(RestTest.class); return suite; } diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java index 5f836942ba..7378df5c36 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java @@ -3,15 +3,22 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.annotation.transactionrequired; import se.scalablesolutions.akka.annotation.prerestart; import se.scalablesolutions.akka.annotation.postrestart; +import se.scalablesolutions.akka.annotation.inittransactionalstate; import se.scalablesolutions.akka.state.*; @transactionrequired public class InMemStateful { - private TransactionalState factory = new TransactionalState(); - private TransactionalMap mapState = factory.newMap(); - private TransactionalVector vectorState = factory.newVector(); - private TransactionalRef refState = factory.newRef(); + private TransactionalMap mapState; + private TransactionalVector vectorState; + private TransactionalRef refState; + @inittransactionalstate + public void init() { + mapState = TransactionalState.newMap(); + vectorState = TransactionalState.newVector(); + refState = TransactionalState.newRef(); + } + public String getMapState(String key) { return (String)mapState.get(key).get(); } diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java index ba13be243c..7bb3826a5a 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java @@ -1,15 +1,21 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.annotation.transactionrequired; +import se.scalablesolutions.akka.annotation.inittransactionalstate; import se.scalablesolutions.akka.state.*; @transactionrequired public class InMemStatefulNested { - private TransactionalState factory = new TransactionalState(); - private TransactionalMap mapState = factory.newMap(); - private TransactionalVector vectorState = factory.newVector(); - private TransactionalRef refState = factory.newRef(); + private TransactionalMap mapState; + private TransactionalVector vectorState; + private TransactionalRef refState; + @inittransactionalstate + public void init() { + mapState = TransactionalState.newMap(); + vectorState = TransactionalState.newVector(); + refState = TransactionalState.newRef(); + } public String getMapState(String key) { return (String)mapState.get(key).get(); diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java index ff1c789e1d..9c1e36f5aa 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/InMemoryStateTest.java @@ -35,8 +35,8 @@ public class InMemoryStateTest extends TestCase { } protected void tearDown() { - conf.stop(); - } + conf.stop(); + } public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() { InMemStateful stateful = conf.getInstance(InMemStateful.class); diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java index 494b59a05c..8ec14fcb91 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentClasher.java @@ -1,11 +1,16 @@ package se.scalablesolutions.akka.api; import se.scalablesolutions.akka.state.*; +import se.scalablesolutions.akka.annotation.inittransactionalstate; public class PersistentClasher { - private PersistentState factory = new PersistentState(); - private PersistentMap state = factory.newMap(new CassandraStorageConfig()); + private PersistentMap state; + @inittransactionalstate + public void init() { + state = PersistentState.newMap(new CassandraStorageConfig()); + } + public String getState(String key) { return (String)state.get(key).get(); } diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java index 94690caa31..02a091c1f6 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStateful.java @@ -1,16 +1,22 @@ package se.scalablesolutions.akka.api; +import se.scalablesolutions.akka.annotation.inittransactionalstate; import se.scalablesolutions.akka.annotation.transactionrequired; import se.scalablesolutions.akka.state.*; @transactionrequired public class PersistentStateful { - private PersistentState factory = new PersistentState(); - private PersistentMap mapState = factory.newMap(new CassandraStorageConfig()); - private PersistentVector vectorState = factory.newVector(new CassandraStorageConfig());; - private PersistentRef refState = factory.newRef(new CassandraStorageConfig()); + private PersistentMap mapState; + private PersistentVector vectorState; + private PersistentRef refState; - + @inittransactionalstate + public void init() { + mapState = PersistentState.newMap(new CassandraStorageConfig()); + vectorState = PersistentState.newVector(new CassandraStorageConfig()); + refState = PersistentState.newRef(new CassandraStorageConfig()); + } + public String getMapState(String key) { return (String) mapState.get(key).get(); } diff --git a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java index 42b13f0313..3251184789 100644 --- a/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java +++ b/akka-fun-test-java/src/test/java/se/scalablesolutions/akka/api/PersistentStatefulNested.java @@ -1,15 +1,21 @@ package se.scalablesolutions.akka.api; +import se.scalablesolutions.akka.annotation.inittransactionalstate; import se.scalablesolutions.akka.annotation.transactionrequired; import se.scalablesolutions.akka.state.*; @transactionrequired public class PersistentStatefulNested { - private PersistentState factory = new PersistentState(); - private PersistentMap mapState = factory.newMap(new CassandraStorageConfig()); - private PersistentVector vectorState = factory.newVector(new CassandraStorageConfig());; - private PersistentRef refState = factory.newRef(new CassandraStorageConfig()); + private PersistentMap mapState; + private PersistentVector vectorState; + private PersistentRef refState; + @inittransactionalstate + public void init() { + mapState = PersistentState.newMap(new CassandraStorageConfig()); + vectorState = PersistentState.newVector(new CassandraStorageConfig()); + refState = PersistentState.newRef(new CassandraStorageConfig()); + } public String getMapState(String key) { return (String) mapState.get(key).get(); diff --git a/akka-persistence/src/test/scala/AllTest.scala b/akka-persistence/src/test/scala/AllTest.scala index 68b5d22235..60374da92d 100644 --- a/akka-persistence/src/test/scala/AllTest.scala +++ b/akka-persistence/src/test/scala/AllTest.scala @@ -8,9 +8,9 @@ import junit.framework.TestSuite object AllTest extends TestCase { def suite(): Test = { val suite = new TestSuite("All Scala tests") - suite.addTestSuite(classOf[CassandraPersistentActorSpec]) - suite.addTestSuite(classOf[MongoPersistentActorSpec]) - suite.addTestSuite(classOf[MongoStorageSpec]) + //suite.addTestSuite(classOf[CassandraPersistentActorSpec]) + //suite.addTestSuite(classOf[MongoPersistentActorSpec]) + //suite.addTestSuite(classOf[MongoStorageSpec]) suite } diff --git a/akka-persistence/src/test/scala/CassandraPersistentActorSpec.scala b/akka-persistence/src/test/scala/CassandraPersistentActorSpec.scala index db755294dc..cfdff7bc3e 100644 --- a/akka-persistence/src/test/scala/CassandraPersistentActorSpec.scala +++ b/akka-persistence/src/test/scala/CassandraPersistentActorSpec.scala @@ -31,15 +31,9 @@ class CassandraPersistentActor extends Actor { timeout = 100000 makeTransactionRequired - private var mapState: PersistentMap = _ - private var vectorState: PersistentVector = _ - private var refState: PersistentRef = _ - - override def initializeTransactionalState = { - mapState = PersistentState.newMap(CassandraStorageConfig()) - vectorState = PersistentState.newVector(CassandraStorageConfig()) - refState = PersistentState.newRef(CassandraStorageConfig()) - } + private lazy val mapState: PersistentMap = PersistentState.newMap(CassandraStorageConfig()) + private lazy val vectorState: PersistentVector = PersistentState.newVector(CassandraStorageConfig()) + private lazy val refState: PersistentRef = PersistentState.newRef(CassandraStorageConfig()) def receive: PartialFunction[Any, Unit] = { case GetMapState(key) => diff --git a/akka-persistence/src/test/scala/MongoPersistentActorSpec.scala b/akka-persistence/src/test/scala/MongoPersistentActorSpec.scala index de422919f9..81eeb13261 100644 --- a/akka-persistence/src/test/scala/MongoPersistentActorSpec.scala +++ b/akka-persistence/src/test/scala/MongoPersistentActorSpec.scala @@ -29,12 +29,8 @@ case object LogSize class BankAccountActor extends Actor { makeTransactionRequired - private var accountState: PersistentMap = _ - private var txnLog: PersistentVector = _ - override def initializeTransactionalState = { - accountState = PersistentState.newMap(MongoStorageConfig()) - txnLog = PersistentState.newVector(MongoStorageConfig()) - } + private lazy val accountState: PersistentMap = PersistentState.newMap(MongoStorageConfig()) + private lazy val txnLog: PersistentVector = PersistentState.newVector(MongoStorageConfig()) def receive: PartialFunction[Any, Unit] = { // check balance diff --git a/akka-persistence/src/test/scala/MongoStorageSpec.scala b/akka-persistence/src/test/scala/MongoStorageSpec.scala index fd0a50ada4..2650b49506 100644 --- a/akka-persistence/src/test/scala/MongoStorageSpec.scala +++ b/akka-persistence/src/test/scala/MongoStorageSpec.scala @@ -1,337 +1,337 @@ -package se.scalablesolutions.akka.state - -import junit.framework.TestCase - -import org.junit.{Test, Before} -import org.junit.Assert._ -import dispatch.json._ -import dispatch.json.Js._ - -class MongoStorageSpec extends TestCase { - - val changeSetV = new scala.collection.mutable.ArrayBuffer[AnyRef] - val changeSetM = new scala.collection.mutable.HashMap[AnyRef, AnyRef] - - override def setUp = { - MongoStorage.coll.drop - } - - @Test - def testVectorInsertForTransactionId = { - changeSetV += "debasish" // string - changeSetV += List(1, 2, 3) // Scala List - changeSetV += List(100, 200) - MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) - assertEquals( - 3, - MongoStorage.getVectorStorageSizeFor("U-A1")) - changeSetV.clear - - // changeSetV should be reinitialized - changeSetV += List(12, 23, 45) - changeSetV += "maulindu" - MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) - assertEquals( - 5, - MongoStorage.getVectorStorageSizeFor("U-A1")) - - // add more to the same changeSetV - changeSetV += "ramanendu" - changeSetV += Map(1 -> "dg", 2 -> "mc") - - // add for a diff transaction - MongoStorage.insertVectorStorageEntriesFor("U-A2", changeSetV.toList) - assertEquals( - 4, - MongoStorage.getVectorStorageSizeFor("U-A2")) - - // previous transaction change set should remain same - assertEquals( - 5, - MongoStorage.getVectorStorageSizeFor("U-A1")) - - // test single element entry - MongoStorage.insertVectorStorageEntryFor("U-A1", Map(1->1, 2->4, 3->9)) - assertEquals( - 6, - MongoStorage.getVectorStorageSizeFor("U-A1")) - } - - @Test - def testVectorFetchForKeys = { - - // initially everything 0 - assertEquals( - 0, - MongoStorage.getVectorStorageSizeFor("U-A2")) - - assertEquals( - 0, - MongoStorage.getVectorStorageSizeFor("U-A1")) - - // get some stuff - changeSetV += "debasish" - changeSetV += List(BigDecimal(12), BigDecimal(13), BigDecimal(14)) - MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) - - assertEquals( - 2, - MongoStorage.getVectorStorageSizeFor("U-A1")) - - val JsString(str) = MongoStorage.getVectorStorageEntryFor("U-A1", 0).asInstanceOf[JsString] - assertEquals("debasish", str) - - import dispatch.json.Js._ - - val l = MongoStorage.getVectorStorageEntryFor("U-A1", 1).asInstanceOf[JsValue] - val num_list = list ! num - val num_list(l0) = l - assertEquals(List(12, 13, 14), l0) - - changeSetV.clear - changeSetV += Map(1->1, 2->4, 3->9) - changeSetV += BigInt(2310) - changeSetV += List(100, 200, 300) - MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) - - assertEquals( - 5, - MongoStorage.getVectorStorageSizeFor("U-A1")) - - val r = - MongoStorage.getVectorStorageRangeFor("U-A1", Some(1), None, 3) - - assertEquals(3, r.size) - val lr = r(0).asInstanceOf[JsValue] - val num_list(l1) = lr - assertEquals(List(12, 13, 14), l1) - } - - @Test - def testVectorFetchForNonExistentKeys = { - try { - MongoStorage.getVectorStorageEntryFor("U-A1", 1) - fail("should throw an exception") - } catch {case e: Predef.NoSuchElementException => {}} - - try { - MongoStorage.getVectorStorageRangeFor("U-A1", Some(2), None, 12) - fail("should throw an exception") - } catch {case e: Predef.NoSuchElementException => {}} - } - - @Test - def testMapInsertForTransactionId = { - case class Foo(no: Int, name: String) - fillMap - - // add some more to changeSet - changeSetM += "5" -> Foo(12, "dg") - changeSetM += "6" -> java.util.Calendar.getInstance.getTime - - // insert all into Mongo - MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) - assertEquals( - 6, - MongoStorage.getMapStorageSizeFor("U-M1")) - - // individual insert api - MongoStorage.insertMapStorageEntryFor("U-M1", "7", "akka") - MongoStorage.insertMapStorageEntryFor("U-M1", "8", List(23, 25)) - assertEquals( - 8, - MongoStorage.getMapStorageSizeFor("U-M1")) - - // add the same changeSet for another transaction - MongoStorage.insertMapStorageEntriesFor("U-M2", changeSetM.toList) - assertEquals( - 6, - MongoStorage.getMapStorageSizeFor("U-M2")) - - // the first transaction should remain the same - assertEquals( - 8, - MongoStorage.getMapStorageSizeFor("U-M1")) - changeSetM.clear - } - - @Test - def testMapContents = { - fillMap - MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) - MongoStorage.getMapStorageEntryFor("U-M1", "2") match { - case Some(x) => { - val JsString(str) = x.asInstanceOf[JsValue] - assertEquals("peter", str) - } - case None => fail("should fetch peter") - } - MongoStorage.getMapStorageEntryFor("U-M1", "4") match { - case Some(x) => { - val num_list = list ! num - val num_list(l0) = x.asInstanceOf[JsValue] - assertEquals(3, l0.size) - } - case None => fail("should fetch list") - } - MongoStorage.getMapStorageEntryFor("U-M1", "3") match { - case Some(x) => { - val num_list = list ! num - val num_list(l0) = x.asInstanceOf[JsValue] - assertEquals(2, l0.size) - } - case None => fail("should fetch list") - } - - // get the entire map - val l: List[Tuple2[AnyRef, AnyRef]] = - MongoStorage.getMapStorageFor("U-M1") - - assertEquals(4, l.size) - assertTrue(l.map(_._1).contains("1")) - assertTrue(l.map(_._1).contains("2")) - assertTrue(l.map(_._1).contains("3")) - assertTrue(l.map(_._1).contains("4")) - - val JsString(str) = l.filter(_._1 == "2").first._2 - assertEquals(str, "peter") - - // trying to fetch for a non-existent transaction will throw - try { - MongoStorage.getMapStorageFor("U-M2") - fail("should throw an exception") - } catch {case e: Predef.NoSuchElementException => {}} - - changeSetM.clear - } - - @Test - def testMapContentsByRange = { - fillMap - changeSetM += "5" -> Map(1 -> "dg", 2 -> "mc") - MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) - - // specify start and count - val l: List[Tuple2[AnyRef, AnyRef]] = - MongoStorage.getMapStorageRangeFor( - "U-M1", Some(Integer.valueOf(2)), None, 3) - - assertEquals(3, l.size) - assertEquals("3", l(0)._1.asInstanceOf[String]) - val lst = l(0)._2.asInstanceOf[JsValue] - val num_list = list ! num - val num_list(l0) = lst - assertEquals(List(100, 200), l0) - assertEquals("4", l(1)._1.asInstanceOf[String]) - val ls = l(1)._2.asInstanceOf[JsValue] - val num_list(l1) = ls - assertEquals(List(10, 20, 30), l1) - - // specify start, finish and count where finish - start == count - assertEquals(3, - MongoStorage.getMapStorageRangeFor( - "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(5)), 3).size) - - // specify start, finish and count where finish - start > count - assertEquals(3, - MongoStorage.getMapStorageRangeFor( - "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(9)), 3).size) - - // do not specify start or finish - assertEquals(3, - MongoStorage.getMapStorageRangeFor( - "U-M1", None, None, 3).size) - - // specify finish and count - assertEquals(3, - MongoStorage.getMapStorageRangeFor( - "U-M1", None, Some(Integer.valueOf(3)), 3).size) - - // specify start, finish and count where finish < start - assertEquals(3, - MongoStorage.getMapStorageRangeFor( - "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(1)), 3).size) - - changeSetM.clear - } - - @Test - def testMapStorageRemove = { - fillMap - changeSetM += "5" -> Map(1 -> "dg", 2 -> "mc") - - MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) - assertEquals(5, - MongoStorage.getMapStorageSizeFor("U-M1")) - - // remove key "3" - MongoStorage.removeMapStorageFor("U-M1", "3") - assertEquals(4, - MongoStorage.getMapStorageSizeFor("U-M1")) - - try { - MongoStorage.getMapStorageEntryFor("U-M1", "3") - fail("should throw exception") - } catch { case e => {}} - - // remove the whole stuff - MongoStorage.removeMapStorageFor("U-M1") - - try { - MongoStorage.getMapStorageFor("U-M1") - fail("should throw exception") - } catch { case e: NoSuchElementException => {}} - - changeSetM.clear - } - - private def fillMap = { - changeSetM += "1" -> "john" - changeSetM += "2" -> "peter" - changeSetM += "3" -> List(100, 200) - changeSetM += "4" -> List(10, 20, 30) - changeSetM - } - - @Test - def testRefStorage = { - MongoStorage.getRefStorageFor("U-R1") match { - case None => - case Some(o) => fail("should be None") - } - - val m = Map("1"->1, "2"->4, "3"->9) - MongoStorage.insertRefStorageFor("U-R1", m) - MongoStorage.getRefStorageFor("U-R1") match { - case None => fail("should not be empty") - case Some(r) => { - val a = r.asInstanceOf[JsValue] - val m1 = Symbol("1") ? num - val m2 = Symbol("2") ? num - val m3 = Symbol("3") ? num - - val m1(n1) = a - val m2(n2) = a - val m3(n3) = a - - assertEquals(n1, 1) - assertEquals(n2, 4) - assertEquals(n3, 9) - } - } - - // insert another one - // the previous one should be replaced - val b = List("100", "jonas") - MongoStorage.insertRefStorageFor("U-R1", b) - MongoStorage.getRefStorageFor("U-R1") match { - case None => fail("should not be empty") - case Some(r) => { - val a = r.asInstanceOf[JsValue] - val str_lst = list ! str - val str_lst(l) = a - assertEquals(b, l) - } - } - } -} +package se.scalablesolutions.akka.state + +import junit.framework.TestCase + +import org.junit.{Test, Before} +import org.junit.Assert._ +import dispatch.json._ +import dispatch.json.Js._ + +class MongoStorageSpec extends TestCase { + + val changeSetV = new scala.collection.mutable.ArrayBuffer[AnyRef] + val changeSetM = new scala.collection.mutable.HashMap[AnyRef, AnyRef] + + override def setUp = { + MongoStorage.coll.drop + } + + @Test + def testVectorInsertForTransactionId = { + changeSetV += "debasish" // string + changeSetV += List(1, 2, 3) // Scala List + changeSetV += List(100, 200) + MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) + assertEquals( + 3, + MongoStorage.getVectorStorageSizeFor("U-A1")) + changeSetV.clear + + // changeSetV should be reinitialized + changeSetV += List(12, 23, 45) + changeSetV += "maulindu" + MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) + assertEquals( + 5, + MongoStorage.getVectorStorageSizeFor("U-A1")) + + // add more to the same changeSetV + changeSetV += "ramanendu" + changeSetV += Map(1 -> "dg", 2 -> "mc") + + // add for a diff transaction + MongoStorage.insertVectorStorageEntriesFor("U-A2", changeSetV.toList) + assertEquals( + 4, + MongoStorage.getVectorStorageSizeFor("U-A2")) + + // previous transaction change set should remain same + assertEquals( + 5, + MongoStorage.getVectorStorageSizeFor("U-A1")) + + // test single element entry + MongoStorage.insertVectorStorageEntryFor("U-A1", Map(1->1, 2->4, 3->9)) + assertEquals( + 6, + MongoStorage.getVectorStorageSizeFor("U-A1")) + } + + @Test + def testVectorFetchForKeys = { + + // initially everything 0 + assertEquals( + 0, + MongoStorage.getVectorStorageSizeFor("U-A2")) + + assertEquals( + 0, + MongoStorage.getVectorStorageSizeFor("U-A1")) + + // get some stuff + changeSetV += "debasish" + changeSetV += List(BigDecimal(12), BigDecimal(13), BigDecimal(14)) + MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) + + assertEquals( + 2, + MongoStorage.getVectorStorageSizeFor("U-A1")) + + val JsString(str) = MongoStorage.getVectorStorageEntryFor("U-A1", 0).asInstanceOf[JsString] + assertEquals("debasish", str) + + import dispatch.json.Js._ + + val l = MongoStorage.getVectorStorageEntryFor("U-A1", 1).asInstanceOf[JsValue] + val num_list = list ! num + val num_list(l0) = l + assertEquals(List(12, 13, 14), l0) + + changeSetV.clear + changeSetV += Map(1->1, 2->4, 3->9) + changeSetV += BigInt(2310) + changeSetV += List(100, 200, 300) + MongoStorage.insertVectorStorageEntriesFor("U-A1", changeSetV.toList) + + assertEquals( + 5, + MongoStorage.getVectorStorageSizeFor("U-A1")) + + val r = + MongoStorage.getVectorStorageRangeFor("U-A1", Some(1), None, 3) + + assertEquals(3, r.size) + val lr = r(0).asInstanceOf[JsValue] + val num_list(l1) = lr + assertEquals(List(12, 13, 14), l1) + } + + @Test + def testVectorFetchForNonExistentKeys = { + try { + MongoStorage.getVectorStorageEntryFor("U-A1", 1) + fail("should throw an exception") + } catch {case e: Predef.NoSuchElementException => {}} + + try { + MongoStorage.getVectorStorageRangeFor("U-A1", Some(2), None, 12) + fail("should throw an exception") + } catch {case e: Predef.NoSuchElementException => {}} + } + + @Test + def testMapInsertForTransactionId = { + case class Foo(no: Int, name: String) + fillMap + + // add some more to changeSet + changeSetM += "5" -> Foo(12, "dg") + changeSetM += "6" -> java.util.Calendar.getInstance.getTime + + // insert all into Mongo + MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) + assertEquals( + 6, + MongoStorage.getMapStorageSizeFor("U-M1")) + + // individual insert api + MongoStorage.insertMapStorageEntryFor("U-M1", "7", "akka") + MongoStorage.insertMapStorageEntryFor("U-M1", "8", List(23, 25)) + assertEquals( + 8, + MongoStorage.getMapStorageSizeFor("U-M1")) + + // add the same changeSet for another transaction + MongoStorage.insertMapStorageEntriesFor("U-M2", changeSetM.toList) + assertEquals( + 6, + MongoStorage.getMapStorageSizeFor("U-M2")) + + // the first transaction should remain the same + assertEquals( + 8, + MongoStorage.getMapStorageSizeFor("U-M1")) + changeSetM.clear + } + + @Test + def testMapContents = { + fillMap + MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) + MongoStorage.getMapStorageEntryFor("U-M1", "2") match { + case Some(x) => { + val JsString(str) = x.asInstanceOf[JsValue] + assertEquals("peter", str) + } + case None => fail("should fetch peter") + } + MongoStorage.getMapStorageEntryFor("U-M1", "4") match { + case Some(x) => { + val num_list = list ! num + val num_list(l0) = x.asInstanceOf[JsValue] + assertEquals(3, l0.size) + } + case None => fail("should fetch list") + } + MongoStorage.getMapStorageEntryFor("U-M1", "3") match { + case Some(x) => { + val num_list = list ! num + val num_list(l0) = x.asInstanceOf[JsValue] + assertEquals(2, l0.size) + } + case None => fail("should fetch list") + } + + // get the entire map + val l: List[Tuple2[AnyRef, AnyRef]] = + MongoStorage.getMapStorageFor("U-M1") + + assertEquals(4, l.size) + assertTrue(l.map(_._1).contains("1")) + assertTrue(l.map(_._1).contains("2")) + assertTrue(l.map(_._1).contains("3")) + assertTrue(l.map(_._1).contains("4")) + + val JsString(str) = l.filter(_._1 == "2").first._2 + assertEquals(str, "peter") + + // trying to fetch for a non-existent transaction will throw + try { + MongoStorage.getMapStorageFor("U-M2") + fail("should throw an exception") + } catch {case e: Predef.NoSuchElementException => {}} + + changeSetM.clear + } + + @Test + def testMapContentsByRange = { + fillMap + changeSetM += "5" -> Map(1 -> "dg", 2 -> "mc") + MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) + + // specify start and count + val l: List[Tuple2[AnyRef, AnyRef]] = + MongoStorage.getMapStorageRangeFor( + "U-M1", Some(Integer.valueOf(2)), None, 3) + + assertEquals(3, l.size) + assertEquals("3", l(0)._1.asInstanceOf[String]) + val lst = l(0)._2.asInstanceOf[JsValue] + val num_list = list ! num + val num_list(l0) = lst + assertEquals(List(100, 200), l0) + assertEquals("4", l(1)._1.asInstanceOf[String]) + val ls = l(1)._2.asInstanceOf[JsValue] + val num_list(l1) = ls + assertEquals(List(10, 20, 30), l1) + + // specify start, finish and count where finish - start == count + assertEquals(3, + MongoStorage.getMapStorageRangeFor( + "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(5)), 3).size) + + // specify start, finish and count where finish - start > count + assertEquals(3, + MongoStorage.getMapStorageRangeFor( + "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(9)), 3).size) + + // do not specify start or finish + assertEquals(3, + MongoStorage.getMapStorageRangeFor( + "U-M1", None, None, 3).size) + + // specify finish and count + assertEquals(3, + MongoStorage.getMapStorageRangeFor( + "U-M1", None, Some(Integer.valueOf(3)), 3).size) + + // specify start, finish and count where finish < start + assertEquals(3, + MongoStorage.getMapStorageRangeFor( + "U-M1", Some(Integer.valueOf(2)), Some(Integer.valueOf(1)), 3).size) + + changeSetM.clear + } + + @Test + def testMapStorageRemove = { + fillMap + changeSetM += "5" -> Map(1 -> "dg", 2 -> "mc") + + MongoStorage.insertMapStorageEntriesFor("U-M1", changeSetM.toList) + assertEquals(5, + MongoStorage.getMapStorageSizeFor("U-M1")) + + // remove key "3" + MongoStorage.removeMapStorageFor("U-M1", "3") + assertEquals(4, + MongoStorage.getMapStorageSizeFor("U-M1")) + + try { + MongoStorage.getMapStorageEntryFor("U-M1", "3") + fail("should throw exception") + } catch { case e => {}} + + // remove the whole stuff + MongoStorage.removeMapStorageFor("U-M1") + + try { + MongoStorage.getMapStorageFor("U-M1") + fail("should throw exception") + } catch { case e: NoSuchElementException => {}} + + changeSetM.clear + } + + private def fillMap = { + changeSetM += "1" -> "john" + changeSetM += "2" -> "peter" + changeSetM += "3" -> List(100, 200) + changeSetM += "4" -> List(10, 20, 30) + changeSetM + } + + @Test + def testRefStorage = { + MongoStorage.getRefStorageFor("U-R1") match { + case None => + case Some(o) => fail("should be None") + } + + val m = Map("1"->1, "2"->4, "3"->9) + MongoStorage.insertRefStorageFor("U-R1", m) + MongoStorage.getRefStorageFor("U-R1") match { + case None => fail("should not be empty") + case Some(r) => { + val a = r.asInstanceOf[JsValue] + val m1 = Symbol("1") ? num + val m2 = Symbol("2") ? num + val m3 = Symbol("3") ? num + + val m1(n1) = a + val m2(n2) = a + val m3(n3) = a + + assertEquals(n1, 1) + assertEquals(n2, 4) + assertEquals(n3, 9) + } + } + + // insert another one + // the previous one should be replaced + val b = List("100", "jonas") + MongoStorage.insertRefStorageFor("U-R1", b) + MongoStorage.getRefStorageFor("U-R1") match { + case None => fail("should not be empty") + case Some(r) => { + val a = r.asInstanceOf[JsValue] + val str_lst = list ! str + val str_lst(l) = a + assertEquals(b, l) + } + } + } +} diff --git a/akka.iws b/akka.iws index 4bd5252974..dc2f513d29 100644 --- a/akka.iws +++ b/akka.iws @@ -2,13 +2,23 @@ - - - - + + + + + + - - + + + + + + + + + + @@ -103,7 +113,26 @@ - + + + + + + + + + + + + + + + + + + + + @@ -120,13 +149,14 @@ @@ -182,6 +212,10 @@ + + + + @@ -208,9 +242,6 @@ - - - @@ -221,7 +252,6 @@ - @@ -547,12 +577,12 @@ - - - - - - + + + + + + localhost @@ -587,24 +617,22 @@ - - + + - - + - @@ -615,6 +643,8 @@ + + @@ -652,11 +682,6 @@ - - - - - @@ -666,91 +691,81 @@ - - - + - - - + - - - + - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/changes.xml b/changes.xml index 5b2cdc37eb..63ecce6eed 100644 --- a/changes.xml +++ b/changes.xml @@ -21,10 +21,12 @@ see http://maven.apache.org/plugins/maven-changes-plugin/usage.html for full gui Transparent JSON serialization of Scala objects based on SJSON MongoDB backed actor example Support for using the Lift Web framework with Actors - Security module for authentication and authorization + Security module for HTTP-based authentication and authorization Support for using Scala XML tags in RESTful Actors (scala-jersey) Support for Comet Actors using Atmosphere AMQP integration; abstracted as actors in a supervisor hierarchy. Impl AMQP 0.9.1 + Rewritten STM, now integrated with Multiverse STM + Added STM API for atomic {..} and or {..} else {..} Modularization of distribution into a thin core (actors, remoting and STM) and the rest in submodules JSON serialization for Java objects (using Jackson) JSON serialization for Scala objects (using SJSON)