From 1e6a4c012dd42894275cf71b60227c6fc827f4af Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Fri, 18 Jun 2010 16:31:25 +0200 Subject: [PATCH 01/38] re #281: Removed type parameter from ActorRef.!! which now returns Option[Any] and added Helpers.narrow and Helpers.narrowSilently. --- .../src/main/scala/actor/ActiveObject.scala | 3 ++- akka-core/src/main/scala/actor/ActorRef.scala | 4 ++-- .../src/main/scala/remote/RemoteServer.scala | 5 ++-- .../src/main/scala/stm/DataFlowVariable.scala | 7 +++--- akka-core/src/main/scala/util/Helpers.scala | 23 ++++++++++++++++++- .../src/test/scala/ActorPatternsTest.scala | 7 +++--- ...rBasedEventDrivenDispatcherActorSpec.scala | 5 ++-- .../src/test/scala/InMemoryActorSpec.scala | 13 ++++++----- ...ThreadEventDrivenDispatcherActorSpec.scala | 3 ++- ...adPoolEventDrivenDispatcherActorSpec.scala | 3 ++- akka-core/src/test/scala/StmSpec.scala | 13 ++++++----- .../src/test/scala/ThreadBasedActorSpec.scala | 5 ++-- 12 files changed, 61 insertions(+), 30 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActiveObject.scala b/akka-core/src/main/scala/actor/ActiveObject.scala index a2d2820cca..8ef49eebda 100644 --- a/akka-core/src/main/scala/actor/ActiveObject.scala +++ b/akka-core/src/main/scala/actor/ActiveObject.scala @@ -11,6 +11,7 @@ import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, Completabl import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.serialization.Serializer import se.scalablesolutions.akka.util._ +import se.scalablesolutions.akka.util.Helpers.narrow import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint} import org.codehaus.aspectwerkz.proxy.Proxy @@ -548,7 +549,7 @@ private[akka] sealed class ActiveObjectAspect { actorRef ! Invocation(joinPoint, true, true, sender, senderFuture) null.asInstanceOf[AnyRef] } else { - val result = actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout) + val result = narrow[AnyRef](actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)) if (result.isDefined) result.get else throw new IllegalStateException("No result defined for invocation [" + joinPoint + "]") } diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index 5f08c7b900..0531a43438 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -285,9 +285,9 @@ trait ActorRef extends TransactionManagement { * If you are sending messages using !! then you have to use self.reply(..) * to send a reply message to the original sender. If not then the sender will block until the timeout expires. */ - def !![T](message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[T] = { + def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = { if (isRunning) { - val future = postMessageToMailboxAndCreateFutureResultWithTimeout[T](message, timeout, sender, None) + val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None) val isActiveObject = message.isInstanceOf[Invocation] if (isActiveObject && message.asInstanceOf[Invocation].isVoid) { future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None) diff --git a/akka-core/src/main/scala/remote/RemoteServer.scala b/akka-core/src/main/scala/remote/RemoteServer.scala index aafe38c910..c374d98863 100644 --- a/akka-core/src/main/scala/remote/RemoteServer.scala +++ b/akka-core/src/main/scala/remote/RemoteServer.scala @@ -11,6 +11,7 @@ import java.util.{Map => JMap} import se.scalablesolutions.akka.actor._ import se.scalablesolutions.akka.util._ +import se.scalablesolutions.akka.util.Helpers.narrow import se.scalablesolutions.akka.remote.protobuf.RemoteProtocol._ import se.scalablesolutions.akka.config.Config.config @@ -369,8 +370,8 @@ class RemoteServerHandler( if (request.getIsOneWay) actorRef.!(message)(sender) else { try { - val resultOrNone = actorRef.!!(message)(sender) - val result: AnyRef = if (resultOrNone.isDefined) resultOrNone.get else null + val resultOrNone = narrow[AnyRef](actorRef.!!(message)(sender)) + val result = if (resultOrNone.isDefined) resultOrNone.get else null log.debug("Returning result from actor invocation [%s]", result) val replyBuilder = RemoteReplyProtocol.newBuilder .setId(request.getId) diff --git a/akka-core/src/main/scala/stm/DataFlowVariable.scala b/akka-core/src/main/scala/stm/DataFlowVariable.scala index 752c71cead..925eb922a5 100644 --- a/akka-core/src/main/scala/stm/DataFlowVariable.scala +++ b/akka-core/src/main/scala/stm/DataFlowVariable.scala @@ -10,6 +10,7 @@ import java.util.concurrent.{ConcurrentLinkedQueue, LinkedBlockingQueue} import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.dispatch.CompletableFuture +import se.scalablesolutions.akka.util.Helpers.narrow /** * Implements Oz-style dataflow (single assignment) variables. @@ -102,10 +103,10 @@ import se.scalablesolutions.akka.dispatch.CompletableFuture else { val out = actorOf(new Out(this)).start blockedReaders.offer(out) - val result = out !! Get + val result = narrow[T](out !! Get) out ! Exit - result.getOrElse(throw new DataFlowVariableException( - "Timed out (after " + TIME_OUT + " milliseconds) while waiting for result")) + if (result.isDefined) result.get + else throw new DataFlowVariableException("Timed out (after " + TIME_OUT + " milliseconds) while waiting for result") } } diff --git a/akka-core/src/main/scala/util/Helpers.scala b/akka-core/src/main/scala/util/Helpers.scala index 4835a4dd05..ccbd896610 100644 --- a/akka-core/src/main/scala/util/Helpers.scala +++ b/akka-core/src/main/scala/util/Helpers.scala @@ -37,5 +37,26 @@ object Helpers extends Logging { }) sb.toString } -} + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException + * if the actual type is not assignable from the given one. + */ + def narrow[T](o: Option[Any]): Option[T] = { + require(o != null, "Option to be narrowed must not be null!") + o.asInstanceOf[Option[T]] + } + + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible + * ClassCastException and return None in that case. + */ + def narrowSilently[T: Manifest](o: Option[Any]): Option[T] = + try { + narrow(o) + } catch { + case e: ClassCastException => + log.warning(e, "Cannot narrow %s to expected type %s!", o, implicitly[Manifest[T]].erasure.getName) + None + } +} diff --git a/akka-core/src/test/scala/ActorPatternsTest.scala b/akka-core/src/test/scala/ActorPatternsTest.scala index 0d4e9b6b08..a130cc2d7c 100644 --- a/akka-core/src/test/scala/ActorPatternsTest.scala +++ b/akka-core/src/test/scala/ActorPatternsTest.scala @@ -5,6 +5,7 @@ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.util.Helpers.narrow import org.scalatest.Suite import org.junit.runner.RunWith @@ -39,9 +40,9 @@ class ActorPatternsTest extends junit.framework.TestCase with Suite with MustMat }.start val result = for { - a <- (d.!![Int](testMsg1,5000)) - b <- (d.!![Int](testMsg2,5000)) - c <- (d.!![Int](testMsg3,5000)) + a <- narrow[Int](d !! (testMsg1,5000)) + b <- narrow[Int](d !! (testMsg2,5000)) + c <- narrow[Int](d !! (testMsg3,5000)) } yield a + b + c result.get must be(21) diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala index f876d59527..8084900de2 100644 --- a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala @@ -4,6 +4,7 @@ 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.util.Helpers.narrow import Actor._ object ExecutorBasedEventDrivenDispatcherActorSpec { @@ -41,8 +42,8 @@ class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result: String = (actor !! ("Hello", 10000)).get - assert("World" === result) + val result = narrow[String](actor !! ("Hello", 10000)) + assert("World" === result.get) actor.stop } diff --git a/akka-core/src/test/scala/InMemoryActorSpec.scala b/akka-core/src/test/scala/InMemoryActorSpec.scala index 814e3fb841..b35973cac9 100644 --- a/akka-core/src/test/scala/InMemoryActorSpec.scala +++ b/akka-core/src/test/scala/InMemoryActorSpec.scala @@ -5,6 +5,7 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.stm.{TransactionalState, TransactionalMap, TransactionalRef, TransactionalVector} +import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object InMemoryActorSpec { @@ -116,7 +117,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetMapStateOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get) } @@ -138,7 +139,7 @@ class InMemoryActorSpec extends JUnitSuite { failer.start stateful ! SetMapStateOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state } @@ -163,7 +164,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetVectorStateOneWay("init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert(2 === (stateful !! GetVectorSize).get) } @@ -186,7 +187,7 @@ class InMemoryActorSpec extends JUnitSuite { val failer = actorOf[InMemFailerActor] failer.start stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert(1 === (stateful !! GetVectorSize).get) } @@ -211,7 +212,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetRefStateOneWay("init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("new state" === (stateful !! GetRefState).get) } @@ -234,7 +235,7 @@ class InMemoryActorSpec extends JUnitSuite { val failer = actorOf[InMemFailerActor] failer.start stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier: Option[CountDownLatch] = stateful !! GetNotifier + val notifier = narrow[CountDownLatch](stateful !! GetNotifier) assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("init" === (stateful !! (GetRefState, 1000000)).get) // check that state is == init state } diff --git a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala index 726f79fa22..78afbe3438 100644 --- a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala @@ -6,6 +6,7 @@ import org.junit.Test import Actor._ import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.util.Helpers.narrow object ReactorBasedSingleThreadEventDrivenDispatcherActorSpec { class TestActor extends Actor { @@ -44,7 +45,7 @@ class ReactorBasedSingleThreadEventDrivenDispatcherActorSpec extends JUnitSuite @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result: String = (actor !! ("Hello", 10000)).get + val result = narrow[String](actor !! ("Hello", 10000)).get assert("World" === result) actor.stop } diff --git a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala index b1f3dae678..94a93a9a95 100644 --- a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala @@ -5,6 +5,7 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object ReactorBasedThreadPoolEventDrivenDispatcherActorSpec { @@ -39,7 +40,7 @@ class ReactorBasedThreadPoolEventDrivenDispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result: String = (actor !! ("Hello", 10000)).get + val result = narrow[String](actor !! ("Hello", 10000)).get assert("World" === result) actor.stop } diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala index 17d4be32bd..5a4459513c 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/StmSpec.scala @@ -1,6 +1,7 @@ package se.scalablesolutions.akka.actor import se.scalablesolutions.akka.stm._ +import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ @@ -89,10 +90,10 @@ class StmSpec extends try { val actor = actorOf[GlobalTransactionVectorTestActor].start actor !! Add(5) - val size1: Int = (actor !! Size).getOrElse(fail("Could not get Vector::size")) + val size1: Int = narrow(actor !! Size).getOrElse(fail("Could not get Vector::size")) size1 should equal(2) actor !! Add(2) - val size2: Int = (actor !! Size).getOrElse(fail("Could not get Vector::size")) + val size2 = narrow[Int](actor !! Size).getOrElse(fail("Could not get Vector::size")) size2 should equal(3) } catch { case e => @@ -108,18 +109,18 @@ class StmSpec extends try { val actor = actorOf[NestedTransactorLevelOneActor].start actor !! Add(2) - val size1: Int = (actor !! Size).getOrElse(fail("Could not get size")) + val size1: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) size1 should equal(2) actor !! Add(7) actor ! "HiLevelOne" - val size2: Int = (actor !! Size).getOrElse(fail("Could not get size")) + val size2: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) size2 should equal(7) actor !! Add(0) actor ! "HiLevelTwo" - val size3: Int = (actor !! Size).getOrElse(fail("Could not get size")) + val size3: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) size3 should equal(0) actor !! Add(3) - val size4: Int = (actor !! Size).getOrElse(fail("Could not get size")) + val size4: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) size4 should equal(3) } catch { case e => diff --git a/akka-core/src/test/scala/ThreadBasedActorSpec.scala b/akka-core/src/test/scala/ThreadBasedActorSpec.scala index d10a39965b..edbd1ca26d 100644 --- a/akka-core/src/test/scala/ThreadBasedActorSpec.scala +++ b/akka-core/src/test/scala/ThreadBasedActorSpec.scala @@ -5,6 +5,7 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers +import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object ThreadBasedActorSpec { @@ -40,8 +41,8 @@ class ThreadBasedActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result: String = (actor !! ("Hello", 10000)).get - assert("World" === result) + val result = narrow[String](actor !! ("Hello", 10000)) + assert("World" === result.get) actor.stop } From 64c6930ce8251e7cde6231f47aa62e6757fd69e4 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Sat, 19 Jun 2010 15:56:29 +0200 Subject: [PATCH 02/38] Producer trait with default implementation of Actor.receive --- akka-camel/src/main/scala/Producer.scala | 5 +++++ akka-camel/src/test/scala/ProducerFeatureTest.scala | 1 - akka-samples/akka-sample-camel/src/main/scala/Actors.scala | 3 --- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/akka-camel/src/main/scala/Producer.scala b/akka-camel/src/main/scala/Producer.scala index 7aca16f956..9a68b8d57f 100644 --- a/akka-camel/src/main/scala/Producer.scala +++ b/akka-camel/src/main/scala/Producer.scala @@ -133,6 +133,11 @@ trait Producer { this: Actor => } } + /** + * Default implementation of Actor.receive + */ + protected def receive = produce + /** * Creates a new in-only Exchange. */ diff --git a/akka-camel/src/test/scala/ProducerFeatureTest.scala b/akka-camel/src/test/scala/ProducerFeatureTest.scala index 6f1f39abeb..eda866e064 100644 --- a/akka-camel/src/test/scala/ProducerFeatureTest.scala +++ b/akka-camel/src/test/scala/ProducerFeatureTest.scala @@ -11,7 +11,6 @@ import se.scalablesolutions.akka.actor.Actor._ object ProducerFeatureTest { class TestProducer(uri: String) extends Actor with Producer { def endpointUri = uri - def receive = produce } } diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala index f9feaa7f4d..7ab8b0dae5 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -32,8 +32,6 @@ class Producer1 extends Actor with Producer { override def oneway = false // default override def async = true // default - - protected def receive = produce } class Consumer1 extends Actor with Consumer with Logging { @@ -102,7 +100,6 @@ class Publisher(name: String, uri: String) extends Actor with Producer { self.id = name def endpointUri = uri override def oneway = true - protected def receive = produce } class PublisherBridge(uri: String, publisher: ActorRef) extends Actor with Consumer { From 8f019ac74f9078242b05c811d1ee4df0da23e114 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Sat, 19 Jun 2010 18:36:32 +0200 Subject: [PATCH 03/38] Enforce commons-codec version 1.4 for akka-core --- project/build/AkkaProject.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 01d228c397..cd40331bbb 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -174,6 +174,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // subprojects class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" + val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" val dispatch_json = "net.databinder" % "dispatch-json_2.8.0.RC3" % "0.7.4" % "compile" val dispatch_htdisttp = "net.databinder" % "dispatch-http_2.8.0.RC3" % "0.7.4" % "compile" From 5bfa786dac4d4efc12e6cb50eae0b8ff1dca22e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Sat, 19 Jun 2010 11:32:55 -0700 Subject: [PATCH 04/38] Fixed bug with stm not being enabled by default when no AKKA_HOME is set. --- akka-core/src/main/scala/stm/TransactionManagement.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index d551f7fd76..a6ec288466 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -20,7 +20,7 @@ class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Tran object TransactionManagement extends TransactionManagement { import se.scalablesolutions.akka.config.Config._ - val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", false)) + val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", true)) val FAIR_TRANSACTIONS = config.getBool("akka.stm.fair", true) val INTERRUPTIBLE = config.getBool("akka.stm.interruptible", true) val MAX_NR_OF_RETRIES = config.getInt("akka.stm.max-nr-of-retries", 1000) From 8fc8246204c7733c3965c6b0f46f1036a47f3b43 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:08:23 +1200 Subject: [PATCH 05/38] Updated to Multiverse 0.6-SNAPSHOT --- .../src/main/scala/stm/Transaction.scala | 66 ++++++++----------- .../main/scala/stm/TransactionalState.scala | 6 +- project/build/AkkaProject.scala | 4 +- 3 files changed, 33 insertions(+), 43 deletions(-) diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index cdcd4ad088..3101ef219d 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -146,19 +146,9 @@ object Transaction { */ def atomic[T](body: => T): T = { new TransactionTemplate[T]() { - def execute(mtx: MultiverseTransaction): T = body - - override def onStart(mtx: MultiverseTransaction) = { - val tx = new Transaction - tx.transaction = Some(mtx) - setTransaction(Some(tx)) - mtx.registerLifecycleListener(new TransactionLifecycleListener() { - def notify(mtx: MultiverseTransaction, event: TransactionLifecycleEvent) = event.name match { - case "postCommit" => tx.commit - case "postAbort" => tx.abort - case _ => {} - } - }) + def execute(mtx: MultiverseTransaction): T = { + Transaction.attach + body } }.execute() } @@ -174,8 +164,8 @@ object Transaction { */ def elseBody[A](firstBody: => A) = new { def orElse(secondBody: => A) = new OrElseTemplate[A] { - def run(t: MultiverseTransaction) = firstBody - def orelserun(t: MultiverseTransaction) = secondBody + def either(t: MultiverseTransaction) = firstBody + def orelse(t: MultiverseTransaction) = secondBody }.execute() } } @@ -261,40 +251,38 @@ object Transaction { var isTopLevelTransaction = false new TransactionTemplate[T]() { def execute(mtx: MultiverseTransaction): T = { + if (!isTransactionSetInScope) createNewTransactionSet + Transaction.attach val result = body - val txSet = getTransactionSetInScope log.trace("Committing transaction [%s]\n\tby joining transaction set [%s]", mtx, txSet) + // FIXME ? txSet.tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS) txSet.joinCommit(mtx) clearTransaction result } - - override def onStart(mtx: MultiverseTransaction) = { - val txSet = - if (!isTransactionSetInScope) { - isTopLevelTransaction = true - createNewTransactionSet - } else getTransactionSetInScope - val tx = new Transaction - tx.begin - tx.transaction = Some(mtx) - setTransaction(Some(tx)) - mtx.registerLifecycleListener(new TransactionLifecycleListener() { - def notify(mtx: MultiverseTransaction, event: TransactionLifecycleEvent) = event.name match { - case "postCommit" => - log.trace("Committing transaction [%s]", mtx) - tx.commit - case "postAbort" => - log.trace("Aborting transaction [%s]", mtx) - tx.abort - case _ => {} - } - }) - } }.execute() } } + + /** + * Attach an Akka-specific Transaction to the current Multiverse transaction. + * Must be called within a Multiverse transaction. + */ + private[akka] def attach = { + val mtx = getRequiredThreadLocalTransaction + val tx = new Transaction + tx.begin + tx.transaction = Some(mtx) + TransactionManagement.transaction.set(Some(tx)) + mtx.registerLifecycleListener(new TransactionLifecycleListener() { + def notify(mtx: MultiverseTransaction, event: TransactionLifecycleEvent) = event match { + case TransactionLifecycleEvent.PostCommit => tx.commit + case TransactionLifecycleEvent.PostAbort => tx.abort + case _ => {} + } + }) + } } /** diff --git a/akka-core/src/main/scala/stm/TransactionalState.scala b/akka-core/src/main/scala/stm/TransactionalState.scala index 8f449db1b1..9f5c012965 100644 --- a/akka-core/src/main/scala/stm/TransactionalState.scala +++ b/akka-core/src/main/scala/stm/TransactionalState.scala @@ -64,11 +64,11 @@ trait Abortable { } object RefFactory { - private val factory = getGlobalStmInstance.getProgrammaticReferenceFactoryBuilder.build + private val factory = getGlobalStmInstance.getProgrammaticRefFactoryBuilder.build - def createRef[T] = factory.atomicCreateReference[T]() + def createRef[T] = factory.atomicCreateRef[T]() - def createRef[T](value: T) = factory.atomicCreateReference(value) + def createRef[T](value: T) = factory.atomicCreateRef(value) } /** diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index cd40331bbb..5f1f2d1264 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -19,7 +19,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val CASSANDRA_VERSION = "0.6.1" val LIFT_VERSION = "2.0-scala280-SNAPSHOT" val SCALATEST_VERSION = "1.2-for-scala-2.8.0.RC3-SNAPSHOT" - val MULTIVERSE_VERSION = "0.5.2" + val MULTIVERSE_VERSION = "0.6-SNAPSHOT" // ------------------------------------------------------------ lazy val deployPath = info.projectPath / "deploy" @@ -65,6 +65,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", javaNetRepo) // val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", sonatypeSnapshotRepo) val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) + def codehausSnapshotRepo = "Codehaus Snapshots" at "http://snapshots.repository.codehaus.org" + val multiverseModuleConfig = ModuleConfiguration("org.multiverse", codehausSnapshotRepo) // ------------------------------------------------------------ // project defintions From ed81b02eb761b7b0a82b0fe50d77c975c0fac1e6 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:12:15 +1200 Subject: [PATCH 06/38] Removed atomic0 - no longer used --- akka-core/src/main/scala/stm/Transaction.scala | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 3101ef219d..c3478c7705 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -49,16 +49,6 @@ trait AtomicTemplate[T] { object Transaction { val idFactory = new AtomicLong(-1L) - /** - * Creates a STM atomic transaction and by-passes all transactions hooks - * such as persistence etc. - * - * Only for internal usage. - */ - private[akka] def atomic0[T](body: => T): T = new TransactionTemplate[T]() { - def execute(mtx: MultiverseTransaction): T = body - }.execute() - /** * Module for "local" transaction management, local in the context of threads. * You should only use these if you do not need to have one transaction span From 3a1d888efaa9c9c16f7881d328b99036afdf517d Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:12:52 +1200 Subject: [PATCH 07/38] Removed AtomicTemplate - new Java API will use Multiverse more directly --- .../src/main/scala/stm/Transaction.scala | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index c3478c7705..eb6607e102 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -27,25 +27,6 @@ class NoTransactionInScopeException extends RuntimeException class TransactionRetryException(message: String) extends RuntimeException(message) class StmConfigurationException(message: String) extends RuntimeException(message) -/** - * FIXDOC: document AtomicTemplate - * AtomicTemplate can be used to create atomic blocks from Java code. - *
- * User newUser = new AtomicTemplate[User]() {
- *   User atomic() {
- *     ... // create user atomically
- *     return user;
- *   }
- * }.execute();
- * 
- */ -trait AtomicTemplate[T] { - def atomic: T - def execute: T = Transaction.Local.atomic { - atomic - } -} - object Transaction { val idFactory = new AtomicLong(-1L) From 07f52e8e3ff9444b29f31413aedb583aef790075 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:16:09 +1200 Subject: [PATCH 08/38] Removed for-comprehensions for transactions --- .../src/main/scala/stm/Transaction.scala | 106 ------------------ 1 file changed, 106 deletions(-) diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index eb6607e102..20b9ceb54e 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -57,61 +57,10 @@ object Transaction { * } * * - * Example of atomic transaction management using for comprehensions (monadic): - * - *
-   * import se.scalablesolutions.akka.stm.Transaction.Local._
-   * for (tx <- Transaction.Local)  {
-   *   ... // do transactional stuff
-   * }
-   *
-   * val result = for (tx <- Transaction.Local) yield  {
-   *   ... // do transactional stuff yielding a result
-   * }
-   * 
- * - * Example of using Transaction and TransactionalRef in for comprehensions (monadic): - * - *
-   * // For example, if you have a List with TransactionalRef
-   * val refs: List[TransactionalRef] = ...
-   *
-   * // You can use them together with Transaction in a for comprehension since
-   * // TransactionalRef is also monadic
-   * for  {
-   *   tx <- Transaction.Local
-   *   ref <- refs
-   * } {
-   *   ... // use the ref inside a transaction
-   * }
-   *
-   * val result = for  {
-   *   tx <- Transaction.Local
-   *   ref <- refs
-   * } yield  {
-   *   ... // use the ref inside a transaction, yield a result
-   * }
-   * 
- * * @author Jonas Bonér */ object Local extends TransactionManagement with Logging { - /** - * See ScalaDoc on Transaction.Local class. - */ - def map[T](f: => T): T = atomic {f} - - /** - * See ScalaDoc on Transaction.Local class. - */ - def flatMap[T](f: => T): T = atomic {f} - - /** - * See ScalaDoc on Transaction.Local class. - */ - def foreach(f: => Unit): Unit = atomic {f} - /** * See ScalaDoc on Transaction.Local class. */ @@ -156,65 +105,10 @@ object Transaction { * } * * - * Example of atomic transaction management using for comprehensions (monadic): - * - *
-   * import se.scalablesolutions.akka.stm.Transaction
-   * for (tx <- Transaction.Global)  {
-   *   ... // do transactional stuff
-   * }
-   *
-   * val result = for (tx <- Transaction.Global) yield  {
-   *   ... // do transactional stuff yielding a result
-   * }
-   * 
- * - * Example of using Transaction and TransactionalRef in for comprehensions (monadic): - * - *
-   * // For example, if you have a List with TransactionalRef
-   * val refs: List[TransactionalRef] = ...
-   *
-   * // You can use them together with Transaction in a for comprehension since
-   * // TransactionalRef is also monadic
-   * for  {
-   *   tx <- Transaction.Global
-   *   ref <- refs
-   * } {
-   *   ... // use the ref inside a transaction
-   * }
-   *
-   * val result = for  {
-   *   tx <- Transaction.Global
-   *   ref <- refs
-   * } yield  {
-   *   ... // use the ref inside a transaction, yield a result
-   * }
-   * 
- * * @author Jonas Bonér */ object Global extends TransactionManagement with Logging { - /** - * See ScalaDoc on Transaction.Global class. - */ - def map[T](f: => T): T = atomic {f} - - /** - * See ScalaDoc on Transaction.Global class. - */ - def flatMap[T](f: => T): T = atomic {f} - - /** - * See ScalaDoc on Transaction.Global class. - */ - def foreach(f: => Unit): Unit = atomic {f} - - -// FIXME tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS) -//getTransactionSetInScope.tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS) - /** * See ScalaDoc on Transaction.Global class. */ From 77960d6571d3e1cab8413f3a94a21a3caca32f7f Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:38:13 +1200 Subject: [PATCH 09/38] Removed TransactionalState and TransactionalRef --- .../akka/api/InMemStateful.java | 8 +- .../akka/api/InMemStatefulNested.java | 8 +- akka-core/src/main/scala/stm/Ref.scala | 150 ++++++++ .../src/main/scala/stm/Transaction.scala | 21 ++ .../src/main/scala/stm/TransactionalMap.scala | 85 +++++ .../main/scala/stm/TransactionalState.scala | 343 ------------------ .../main/scala/stm/TransactionalVector.scala | 60 +++ .../src/test/scala/InMemoryActorSpec.scala | 8 +- akka-core/src/test/scala/StmSpec.scala | 8 +- .../src/main/scala/Storage.scala | 28 +- .../src/main/scala/Ants.scala | 1 - .../src/main/scala/akka/SimpleService.scala | 4 +- .../java/sample/rest/java/SimpleService.java | 3 +- .../src/main/scala/SimpleService.scala | 4 +- .../src/main/scala/SimpleService.scala | 4 +- .../akka/spring/foo/StatefulPojo.java | 11 +- 16 files changed, 358 insertions(+), 388 deletions(-) create mode 100644 akka-core/src/main/scala/stm/Ref.scala create mode 100644 akka-core/src/main/scala/stm/TransactionalMap.scala delete mode 100644 akka-core/src/main/scala/stm/TransactionalState.scala create mode 100644 akka-core/src/main/scala/stm/TransactionalVector.scala diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java index afe2f2e232..411fc1d420 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStateful.java @@ -10,14 +10,14 @@ import se.scalablesolutions.akka.stm.*; public class InMemStateful { private TransactionalMap mapState; private TransactionalVector vectorState; - private TransactionalRef refState; + private Ref refState; private boolean isInitialized = false; public void init() { if (!isInitialized) { - mapState = TransactionalState.newMap(); - vectorState = TransactionalState.newVector(); - refState = TransactionalState.newRef(); + mapState = new TransactionalMap(); + vectorState = new TransactionalVector(); + refState = new Ref(); isInitialized = true; } } diff --git a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java index 932dc2c162..424e2c03e0 100644 --- a/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java +++ b/akka-active-object-test/src/test/java/se/scalablesolutions/akka/api/InMemStatefulNested.java @@ -8,14 +8,14 @@ import se.scalablesolutions.akka.stm.*; public class InMemStatefulNested { private TransactionalMap mapState; private TransactionalVector vectorState; - private TransactionalRef refState; + private Ref refState; private boolean isInitialized = false; public void init() { if (!isInitialized) { - mapState = TransactionalState.newMap(); - vectorState = TransactionalState.newVector(); - refState = TransactionalState.newRef(); + mapState = new TransactionalMap(); + vectorState = new TransactionalVector(); + refState = new Ref(); isInitialized = true; } } diff --git a/akka-core/src/main/scala/stm/Ref.scala b/akka-core/src/main/scala/stm/Ref.scala new file mode 100644 index 0000000000..3b13d32971 --- /dev/null +++ b/akka-core/src/main/scala/stm/Ref.scala @@ -0,0 +1,150 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.UUID + +import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance + +object RefFactory { + private val factory = getGlobalStmInstance.getProgrammaticRefFactoryBuilder.build + + def createRef[T] = factory.atomicCreateRef[T]() + + def createRef[T](value: T) = factory.atomicCreateRef(value) +} + +/** + * Ref. + * + * @author Jonas Bonér + */ +object Ref { + def apply[T]() = new Ref[T] + + def apply[T](initialValue: T) = new Ref[T](Some(initialValue)) + + /** + * An implicit conversion that converts a Ref to an Iterable value. + */ + implicit def ref2Iterable[T](ref: Ref[T]): Iterable[T] = ref.toList +} + +/** + * Implements a transactional managed reference. + * + * @author Jonas Bonér + */ +class Ref[T](initialOpt: Option[T] = None) extends Transactional { + self => + + def this() = this(None) // Java compatibility + + import org.multiverse.api.ThreadLocalTransaction._ + + val uuid = UUID.newUuid.toString + + private[this] val ref = { + if (initialOpt.isDefined) RefFactory.createRef(initialOpt.get) + else RefFactory.createRef[T] + } + + def swap(elem: T) = { + ensureIsInTransaction + ref.set(elem) + } + + def alter(f: T => T): T = { + ensureIsInTransaction + ensureNotNull + ref.set(f(ref.get)) + ref.get + } + + def get: Option[T] = { + ensureIsInTransaction + if (ref.isNull) None + else Some(ref.get) + } + + def getOrWait: T = { + ensureIsInTransaction + ref.getOrAwait + } + + def getOrElse(default: => T): T = { + ensureIsInTransaction + if (ref.isNull) default + else ref.get + } + + def isDefined: Boolean = { + ensureIsInTransaction + !ref.isNull + } + + def isEmpty: Boolean = { + ensureIsInTransaction + ref.isNull + } + + def map[B](f: T => B): Ref[B] = { + ensureIsInTransaction + if (isEmpty) Ref[B] else Ref(f(ref.get)) + } + + def flatMap[B](f: T => Ref[B]): Ref[B] = { + ensureIsInTransaction + if (isEmpty) Ref[B] else f(ref.get) + } + + def filter(p: T => Boolean): Ref[T] = { + ensureIsInTransaction + if (isDefined && p(ref.get)) Ref(ref.get) else Ref[T] + } + + /** + * Necessary to keep from being implicitly converted to Iterable in for comprehensions. + */ + def withFilter(p: T => Boolean): WithFilter = new WithFilter(p) + + class WithFilter(p: T => Boolean) { + def map[B](f: T => B): Ref[B] = self filter p map f + def flatMap[B](f: T => Ref[B]): Ref[B] = self filter p flatMap f + def foreach[U](f: T => U): Unit = self filter p foreach f + def withFilter(q: T => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) + } + + def foreach[U](f: T => U): Unit = { + ensureIsInTransaction + if (isDefined) f(ref.get) + } + + def elements: Iterator[T] = { + ensureIsInTransaction + if (isEmpty) Iterator.empty else Iterator(ref.get) + } + + def toList: List[T] = { + ensureIsInTransaction + if (isEmpty) List() else List(ref.get) + } + + def toRight[X](left: => X) = { + ensureIsInTransaction + if (isEmpty) Left(left) else Right(ref.get) + } + + def toLeft[X](right: => X) = { + ensureIsInTransaction + if (isEmpty) Right(right) else Left(ref.get) + } + + private def ensureIsInTransaction = + if (getThreadLocalTransaction eq null) throw new NoTransactionInScopeException + + private def ensureNotNull = + if (ref.isNull) throw new RuntimeException("Cannot alter Ref's value when it is null") +} diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 20b9ceb54e..7c1e9010a8 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -265,3 +265,24 @@ object TransactionStatus { case object Completed extends TransactionStatus } +/** + * @author Jonas Bonér + */ +@serializable +trait Transactional { + val uuid: String +} + +/** + * @author Jonas Bonér + */ +trait Committable { + def commit: Unit +} + +/** + * @author Jonas Bonér + */ +trait Abortable { + def abort: Unit +} diff --git a/akka-core/src/main/scala/stm/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala new file mode 100644 index 0000000000..def4838b37 --- /dev/null +++ b/akka-core/src/main/scala/stm/TransactionalMap.scala @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.UUID + +import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance + +object TransactionalMap { + def apply[K, V]() = new TransactionalMap[K, V] + + def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashTrie(pairs: _*))) +} + +/** + * Implements an in-memory transactional Map based on Clojure's PersistentMap. + * + * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. + * + * @author Jonas Bonér + */ +class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] { + def this() = this(None) // Java compatibility + + val uuid = UUID.newUuid.toString + + protected[this] val ref = new Ref(initialOpt.orElse(Some(new HashTrie[K, V]))) + + def -=(key: K) = { + remove(key) + this + } + + def +=(key: K, value: V) = put(key, value) + + def +=(kv: (K, V)) = { + put(kv._1,kv._2) + this + } + + override def remove(key: K) = { + val map = ref.get.get + val oldValue = map.get(key) + ref.swap(ref.get.get - key) + oldValue + } + + def get(key: K): Option[V] = ref.get.get.get(key) + + override def put(key: K, value: V): Option[V] = { + val map = ref.get.get + val oldValue = map.get(key) + ref.swap(map.update(key, value)) + oldValue + } + + override def update(key: K, value: V) = { + val map = ref.get.get + val oldValue = map.get(key) + ref.swap(map.update(key, value)) + } + + def iterator = ref.get.get.iterator + + override def elements: Iterator[(K, V)] = ref.get.get.iterator + + override def contains(key: K): Boolean = ref.get.get.contains(key) + + override def clear = ref.swap(new HashTrie[K, V]) + + override def size: Int = ref.get.get.size + + override def hashCode: Int = System.identityHashCode(this); + + override def equals(other: Any): Boolean = + other.isInstanceOf[TransactionalMap[_, _]] && + other.hashCode == hashCode + + override def toString = if (outsideTransaction) "" else super.toString + + def outsideTransaction = + org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null +} diff --git a/akka-core/src/main/scala/stm/TransactionalState.scala b/akka-core/src/main/scala/stm/TransactionalState.scala deleted file mode 100644 index 9f5c012965..0000000000 --- a/akka-core/src/main/scala/stm/TransactionalState.scala +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.stm - -import se.scalablesolutions.akka.util.UUID - -import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance - -/** - * Example Scala usage: - *
- * val myMap = TransactionalState.newMap
- * val myVector = TransactionalState.newVector
- * val myRef = TransactionalState.newRef
- * 
- * Or: - *
- * val myMap = TransactionalMap()
- * val myVector = TransactionalVector()
- * val myRef = TransactionalRef()
- * 
- * - *

- * Example Java usage: - *

- * TransactionalMap myMap = TransactionalState.newMap();
- * 
- * - * @author Jonas Bonér - */ -object TransactionalState { - def newMap[K, V] = TransactionalMap[K, V]() - def newMap[K, V](pairs: (K, V)*) = TransactionalMap(pairs: _*) - - def newVector[T] = TransactionalVector[T]() - def newVector[T](elems: T*) = TransactionalVector(elems :_*) - - def newRef[T] = TransactionalRef[T]() - def newRef[T](initialValue: T) = TransactionalRef(initialValue) -} - -/** - * @author Jonas Bonér - */ -@serializable -trait Transactional { - val uuid: String -} - -/** - * @author Jonas Bonér - */ -trait Committable { - def commit: Unit -} - -/** - * @author Jonas Bonér - */ -trait Abortable { - def abort: Unit -} - -object RefFactory { - private val factory = getGlobalStmInstance.getProgrammaticRefFactoryBuilder.build - - def createRef[T] = factory.atomicCreateRef[T]() - - def createRef[T](value: T) = factory.atomicCreateRef(value) -} - -/** - * Alias to TransactionalRef. - * - * @author Jonas Bonér - */ -object Ref { - type Ref[T] = TransactionalRef[T] - - def apply[T]() = new Ref[T] - - def apply[T](initialValue: T) = new Ref[T](Some(initialValue)) -} - -/** - * Alias to Ref. - * - * @author Jonas Bonér - */ -object TransactionalRef { - - /** - * An implicit conversion that converts a TransactionalRef to an Iterable value. - */ - implicit def ref2Iterable[T](ref: TransactionalRef[T]): Iterable[T] = ref.toList - - def apply[T]() = new TransactionalRef[T] - - def apply[T](initialValue: T) = new TransactionalRef[T](Some(initialValue)) -} - -/** - * Implements a transactional managed reference. - * Alias to Ref. - * - * @author Jonas Bonér - */ -class TransactionalRef[T](initialOpt: Option[T] = None) extends Transactional { - self => - - import org.multiverse.api.ThreadLocalTransaction._ - - implicit val txInitName = "TransactionalRef:Init" - val uuid = UUID.newUuid.toString - - private[this] val ref = { - if (initialOpt.isDefined) RefFactory.createRef(initialOpt.get) - else RefFactory.createRef[T] - } - - def swap(elem: T) = { - ensureIsInTransaction - ref.set(elem) - } - - def alter(f: T => T): T = { - ensureIsInTransaction - ensureNotNull - ref.set(f(ref.get)) - ref.get - } - - def get: Option[T] = { - ensureIsInTransaction - if (ref.isNull) None - else Some(ref.get) - } - - def getOrWait: T = { - ensureIsInTransaction - ref.getOrAwait - } - - def getOrElse(default: => T): T = { - ensureIsInTransaction - if (ref.isNull) default - else ref.get - } - - def isDefined: Boolean = { - ensureIsInTransaction - !ref.isNull - } - - def isEmpty: Boolean = { - ensureIsInTransaction - ref.isNull - } - - def map[B](f: T => B): TransactionalRef[B] = { - ensureIsInTransaction - if (isEmpty) TransactionalRef[B] else TransactionalRef(f(ref.get)) - } - - def flatMap[B](f: T => TransactionalRef[B]): TransactionalRef[B] = { - ensureIsInTransaction - if (isEmpty) TransactionalRef[B] else f(ref.get) - } - - def filter(p: T => Boolean): TransactionalRef[T] = { - ensureIsInTransaction - if (isDefined && p(ref.get)) TransactionalRef(ref.get) else TransactionalRef[T] - } - - /** - * Necessary to keep from being implicitly converted to Iterable in for comprehensions. - */ - def withFilter(p: T => Boolean): WithFilter = new WithFilter(p) - - class WithFilter(p: T => Boolean) { - def map[B](f: T => B): TransactionalRef[B] = self filter p map f - def flatMap[B](f: T => TransactionalRef[B]): TransactionalRef[B] = self filter p flatMap f - def foreach[U](f: T => U): Unit = self filter p foreach f - def withFilter(q: T => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) - } - - def foreach[U](f: T => U): Unit = { - ensureIsInTransaction - if (isDefined) f(ref.get) - } - - def elements: Iterator[T] = { - ensureIsInTransaction - if (isEmpty) Iterator.empty else Iterator(ref.get) - } - - def toList: List[T] = { - ensureIsInTransaction - if (isEmpty) List() else List(ref.get) - } - - def toRight[X](left: => X) = { - ensureIsInTransaction - if (isEmpty) Left(left) else Right(ref.get) - } - - def toLeft[X](right: => X) = { - ensureIsInTransaction - if (isEmpty) Right(right) else Left(ref.get) - } - - private def ensureIsInTransaction = - ()// if (getThreadLocalTransaction eq null) throw new NoTransactionInScopeException - - private def ensureNotNull = - if (ref.isNull) throw new RuntimeException("Cannot alter Ref's value when it is null") -} - -object TransactionalMap { - def apply[K, V]() = new TransactionalMap[K, V] - - def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashTrie(pairs: _*))) -} - -/** - * Implements an in-memory transactional Map based on Clojure's PersistentMap. - * - * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. - * - * @author Jonas Bonér - */ -class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] { - val uuid = UUID.newUuid.toString - - protected[this] val ref = new TransactionalRef(initialOpt.orElse(Some(new HashTrie[K, V]))) - - def -=(key: K) = { - remove(key) - this - } - - def +=(key: K, value: V) = put(key, value) - - def +=(kv: (K, V)) = { - put(kv._1,kv._2) - this - } - - override def remove(key: K) = { - val map = ref.get.get - val oldValue = map.get(key) - ref.swap(ref.get.get - key) - oldValue - } - - def get(key: K): Option[V] = ref.get.get.get(key) - - override def put(key: K, value: V): Option[V] = { - val map = ref.get.get - val oldValue = map.get(key) - ref.swap(map.update(key, value)) - oldValue - } - - override def update(key: K, value: V) = { - val map = ref.get.get - val oldValue = map.get(key) - ref.swap(map.update(key, value)) - } - - def iterator = ref.get.get.iterator - - override def elements: Iterator[(K, V)] = ref.get.get.iterator - - override def contains(key: K): Boolean = ref.get.get.contains(key) - - override def clear = ref.swap(new HashTrie[K, V]) - - override def size: Int = ref.get.get.size - - override def hashCode: Int = System.identityHashCode(this); - - override def equals(other: Any): Boolean = - other.isInstanceOf[TransactionalMap[_, _]] && - other.hashCode == hashCode - - override def toString = if (outsideTransaction) "" else super.toString - - def outsideTransaction = - org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null -} - -object TransactionalVector { - def apply[T]() = new TransactionalVector[T] - - def apply[T](elems: T*) = new TransactionalVector(Some(Vector(elems: _*))) -} - -/** - * Implements an in-memory transactional Vector based on Clojure's PersistentVector. - * - * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. - * - * @author Jonas Bonér - */ -class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] { - val uuid = UUID.newUuid.toString - - private[this] val ref = new TransactionalRef(initialOpt.orElse(Some(EmptyVector))) - - def clear = ref.swap(EmptyVector) - - def +(elem: T) = add(elem) - - def add(elem: T) = ref.swap(ref.get.get + elem) - - def get(index: Int): T = ref.get.get.apply(index) - - /** - * Removes the tail element of this vector. - */ - def pop = ref.swap(ref.get.get.pop) - - def update(index: Int, elem: T) = ref.swap(ref.get.get.update(index, elem)) - - def length: Int = ref.get.get.length - - def apply(index: Int): T = ref.get.get.apply(index) - - override def hashCode: Int = System.identityHashCode(this); - - override def equals(other: Any): Boolean = - other.isInstanceOf[TransactionalVector[_]] && - other.hashCode == hashCode - - override def toString = if (outsideTransaction) "" else super.toString - - def outsideTransaction = - org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null -} - diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala new file mode 100644 index 0000000000..5c219f7a7f --- /dev/null +++ b/akka-core/src/main/scala/stm/TransactionalVector.scala @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.UUID + +import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance + +object TransactionalVector { + def apply[T]() = new TransactionalVector[T] + + def apply[T](elems: T*) = new TransactionalVector(Some(Vector(elems: _*))) +} + +/** + * Implements an in-memory transactional Vector based on Clojure's PersistentVector. + * + * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. + * + * @author Jonas Bonér + */ +class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] { + def this() = this(None) // Java compatibility + + val uuid = UUID.newUuid.toString + + private[this] val ref = new Ref(initialOpt.orElse(Some(EmptyVector))) + + def clear = ref.swap(EmptyVector) + + def +(elem: T) = add(elem) + + def add(elem: T) = ref.swap(ref.get.get + elem) + + def get(index: Int): T = ref.get.get.apply(index) + + /** + * Removes the tail element of this vector. + */ + def pop = ref.swap(ref.get.get.pop) + + def update(index: Int, elem: T) = ref.swap(ref.get.get.update(index, elem)) + + def length: Int = ref.get.get.length + + def apply(index: Int): T = ref.get.get.apply(index) + + override def hashCode: Int = System.identityHashCode(this); + + override def equals(other: Any): Boolean = + other.isInstanceOf[TransactionalVector[_]] && + other.hashCode == hashCode + + override def toString = if (outsideTransaction) "" else super.toString + + def outsideTransaction = + org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null +} diff --git a/akka-core/src/test/scala/InMemoryActorSpec.scala b/akka-core/src/test/scala/InMemoryActorSpec.scala index 814e3fb841..3f9ec33cb3 100644 --- a/akka-core/src/test/scala/InMemoryActorSpec.scala +++ b/akka-core/src/test/scala/InMemoryActorSpec.scala @@ -4,7 +4,7 @@ import java.util.concurrent.{TimeUnit, CountDownLatch} import org.scalatest.junit.JUnitSuite import org.junit.Test -import se.scalablesolutions.akka.stm.{TransactionalState, TransactionalMap, TransactionalRef, TransactionalVector} +import se.scalablesolutions.akka.stm.{Ref, TransactionalMap, TransactionalVector} import Actor._ object InMemoryActorSpec { @@ -35,9 +35,9 @@ class InMemStatefulActor(expectedInvocationCount: Int) extends Transactor { val notifier = new CountDownLatch(expectedInvocationCount) - private lazy val mapState = TransactionalState.newMap[String, String] - private lazy val vectorState = TransactionalState.newVector[String] - private lazy val refState = TransactionalState.newRef[String] + private lazy val mapState = TransactionalMap[String, String]() + private lazy val vectorState = TransactionalVector[String]() + private lazy val refState = Ref[String]() def receive = { case GetNotifier => diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala index b7537b83b2..4390088185 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/StmSpec.scala @@ -21,7 +21,7 @@ class StmSpec extends it("should be able to do multiple consecutive atomic {..} statements") { import Transaction.Local._ - lazy val ref = TransactionalState.newRef[Int] + lazy val ref = Ref[Int]() def increment = atomic { ref.swap(ref.get.getOrElse(0) + 1) @@ -40,7 +40,7 @@ class StmSpec extends it("should be able to do nested atomic {..} statements") { import Transaction.Local._ - lazy val ref = TransactionalState.newRef[Int] + lazy val ref = Ref[Int]() def increment = atomic { ref.swap(ref.get.getOrElse(0) + 1) @@ -62,7 +62,7 @@ class StmSpec extends it("should roll back failing nested atomic {..} statements") { import Transaction.Local._ - lazy val ref = TransactionalState.newRef[Int] + lazy val ref = Ref[Int]() def increment = atomic { ref.swap(ref.get.getOrElse(0) + 1) @@ -213,7 +213,7 @@ class NestedTransactorLevelOneActor extends Actor { } } -class NestedTransactorLevelTwoActor extends Actor { +class NestedTransactorLevelTwoActor extends Transactor { import GlobalTransactionVectorTestActor._ private val ref = Ref(0) diff --git a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala index 6a0eb9a8d8..135ee584b9 100644 --- a/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala +++ b/akka-persistence/akka-persistence-common/src/main/scala/Storage.scala @@ -82,9 +82,9 @@ trait Storage { */ trait PersistentMap[K, V] extends scala.collection.mutable.Map[K, V] with Transactional with Committable with Abortable with Logging { - protected val newAndUpdatedEntries = TransactionalState.newMap[K, V] - protected val removedEntries = TransactionalState.newVector[K] - protected val shouldClearOnCommit = TransactionalRef[Boolean]() + protected val newAndUpdatedEntries = TransactionalMap[K, V]() + protected val removedEntries = TransactionalVector[K]() + protected val shouldClearOnCommit = Ref[Boolean]() // to be concretized in subclasses val storage: MapStorageBackend[K, V] @@ -195,10 +195,10 @@ trait PersistentMap[K, V] extends scala.collection.mutable.Map[K, V] * @author Jonas Bonér */ trait PersistentVector[T] extends IndexedSeq[T] with Transactional with Committable with Abortable { - protected val newElems = TransactionalState.newVector[T] - protected val updatedElems = TransactionalState.newMap[Int, T] - protected val removedElems = TransactionalState.newVector[T] - protected val shouldClearOnCommit = TransactionalRef[Boolean]() + protected val newElems = TransactionalVector[T]() + protected val updatedElems = TransactionalMap[Int, T]() + protected val removedElems = TransactionalVector[T]() + protected val shouldClearOnCommit = Ref[Boolean]() val storage: VectorStorageBackend[T] @@ -276,7 +276,7 @@ trait PersistentVector[T] extends IndexedSeq[T] with Transactional with Committa * @author Jonas Bonér */ trait PersistentRef[T] extends Transactional with Committable with Abortable { - protected val ref = new TransactionalRef[T] + protected val ref = Ref[T]() val storage: RefStorageBackend[T] @@ -343,14 +343,14 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] import scala.collection.immutable.Queue // current trail that will be played on commit to the underlying store - protected val enqueuedNDequeuedEntries = TransactionalState.newVector[(Option[A], QueueOp)] - protected val shouldClearOnCommit = TransactionalRef[Boolean]() + protected val enqueuedNDequeuedEntries = TransactionalVector[(Option[A], QueueOp)]() + protected val shouldClearOnCommit = Ref[Boolean]() // local queue that will record all enqueues and dequeues in the current txn - protected val localQ = TransactionalRef[Queue[A]]() + protected val localQ = Ref[Queue[A]]() // keeps a pointer to the underlying storage for the enxt candidate to be dequeued - protected val pickMeForDQ = TransactionalRef[Int]() + protected val pickMeForDQ = Ref[Int]() localQ.swap(Queue.empty) pickMeForDQ.swap(0) @@ -481,8 +481,8 @@ trait PersistentQueue[A] extends scala.collection.mutable.Queue[A] */ trait PersistentSortedSet[A] extends Transactional with Committable with Abortable { - protected val newElems = TransactionalState.newMap[A, Float] - protected val removedElems = TransactionalState.newVector[A] + protected val newElems = TransactionalMap[A, Float]() + protected val removedElems = TransactionalVector[A]() val storage: SortedSetStorageBackend[A] diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala index 05fe245b10..bac99f58c6 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -10,7 +10,6 @@ import se.scalablesolutions.akka import akka.actor.{ActorRef, Transactor, Scheduler} import akka.actor.Actor.actorOf import akka.stm.{Vector => _, _} -import akka.stm.Ref.Ref import akka.stm.Transaction.Local._ object Config { diff --git a/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala b/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala index 33037b8d8b..7557404da9 100644 --- a/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala +++ b/akka-samples/akka-sample-lift/src/main/scala/akka/SimpleService.scala @@ -2,7 +2,7 @@ package sample.lift import se.scalablesolutions.akka.actor.{Transactor, Actor} import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.stm.TransactionalState +import se.scalablesolutions.akka.stm.TransactionalMap import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage import Actor._ @@ -22,7 +22,7 @@ class SimpleService extends Transactor { case object Tick private val KEY = "COUNTER" private var hasStartedTicking = false - private lazy val storage = TransactionalState.newMap[String, Integer] + private lazy val storage = TransactionalMap[String, Integer]() @GET @Produces(Array("text/html")) diff --git a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java index 44d23e873c..097ba810b5 100644 --- a/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java +++ b/akka-samples/akka-sample-rest-java/src/main/java/sample/rest/java/SimpleService.java @@ -9,7 +9,6 @@ import se.scalablesolutions.akka.actor.ActiveObjectContext; 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.stm.TransactionalState; import se.scalablesolutions.akka.stm.TransactionalMap; @transactionrequired @@ -21,7 +20,7 @@ public class SimpleService { private Receiver receiver = ActiveObject.newInstance(Receiver.class); public String count() { - if (storage == null) storage = TransactionalState.newMap(); + if (storage == null) storage = new TransactionalMap(); if (!hasStartedTicking) { storage.put(KEY, 0); hasStartedTicking = true; diff --git a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala index 90e208d7e1..0a5af80a57 100644 --- a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala @@ -6,7 +6,7 @@ package sample.rest.scala import se.scalablesolutions.akka.actor.{Transactor, SupervisorFactory, Actor} import se.scalablesolutions.akka.actor.Actor._ -import se.scalablesolutions.akka.stm.TransactionalState +import se.scalablesolutions.akka.stm.TransactionalMap import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.util.Logging @@ -63,7 +63,7 @@ class SimpleService { class SimpleServiceActor extends Transactor { private val KEY = "COUNTER" private var hasStartedTicking = false - private lazy val storage = TransactionalState.newMap[String, Integer] + private lazy val storage = TransactionalMap[String, Integer]() def receive = { case "Tick" => if (hasStartedTicking) { diff --git a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala index e6892c7b62..061555ef05 100644 --- a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala @@ -9,7 +9,7 @@ import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.security.{BasicAuthenticationActor,BasicCredentials,SpnegoAuthenticationActor,DigestAuthenticationActor, UserInfo} -import se.scalablesolutions.akka.stm.TransactionalState +import se.scalablesolutions.akka.stm.TransactionalMap import se.scalablesolutions.akka.actor.ActorRegistry.actorsFor class Boot { @@ -135,7 +135,7 @@ class SecureTickService { class SecureTickActor extends Transactor with Logging { private val KEY = "COUNTER" private var hasStartedTicking = false - private lazy val storage = TransactionalState.newMap[String, Integer] + private lazy val storage = TransactionalMap[String, Integer]() def receive = { case "Tick" => if (hasStartedTicking) { val counter = storage.get(KEY).get.intValue diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java index f2308e194f..17332c696a 100644 --- a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java +++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java @@ -3,21 +3,20 @@ package se.scalablesolutions.akka.spring.foo; import se.scalablesolutions.akka.actor.annotation.inittransactionalstate; import se.scalablesolutions.akka.stm.TransactionalMap; import se.scalablesolutions.akka.stm.TransactionalVector; -import se.scalablesolutions.akka.stm.TransactionalRef; -import se.scalablesolutions.akka.stm.TransactionalState; +import se.scalablesolutions.akka.stm.Ref; public class StatefulPojo { private TransactionalMap mapState; private TransactionalVector vectorState; - private TransactionalRef refState; + private Ref refState; private boolean isInitialized = false; @inittransactionalstate public void init() { if (!isInitialized) { - mapState = TransactionalState.newMap(); - vectorState = TransactionalState.newVector(); - refState = TransactionalState.newRef(); + mapState = new TransactionalMap(); + vectorState = new TransactionalVector(); + refState = new Ref(); isInitialized = true; } } From 3cb6e55c699d89f7a64335f3d4d06dd3896f62c5 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:46:58 +1200 Subject: [PATCH 10/38] Using Scala library HashMap and Vector --- akka-core/src/main/scala/stm/HashTrie.scala | 364 ------------------ .../src/main/scala/stm/TransactionalMap.scala | 25 +- .../main/scala/stm/TransactionalVector.scala | 21 +- akka-core/src/main/scala/stm/Vector.scala | 353 ----------------- .../src/test/scala/VectorBugTestSuite.scala | 17 - 5 files changed, 23 insertions(+), 757 deletions(-) delete mode 100644 akka-core/src/main/scala/stm/HashTrie.scala delete mode 100644 akka-core/src/main/scala/stm/Vector.scala delete mode 100644 akka-core/src/test/scala/VectorBugTestSuite.scala diff --git a/akka-core/src/main/scala/stm/HashTrie.scala b/akka-core/src/main/scala/stm/HashTrie.scala deleted file mode 100644 index b1cd992428..0000000000 --- a/akka-core/src/main/scala/stm/HashTrie.scala +++ /dev/null @@ -1,364 +0,0 @@ -/** - Copyright (c) 2007-2008, Rich Hickey - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Clojure nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - */ - -package se.scalablesolutions.akka.stm - -trait PersistentDataStructure - -/** - * A clean-room port of Rich Hickey's persistent hash trie implementation from - * Clojure (http://clojure.org). Originally presented as a mutable structure in - * a paper by Phil Bagwell. - * - * @author Daniel Spiewak - * @author Rich Hickey - */ -@serializable -final class HashTrie[K, +V] private (root: Node[K, V]) extends Map[K, V] with PersistentDataStructure { - override lazy val size = root.size - - def this() = this(new EmptyNode[K]) - - def get(key: K) = root(key, key.hashCode) - - override def +[A >: V](pair: (K, A)) = update(pair._1, pair._2) - - override def update[A >: V](key: K, value: A) = new HashTrie(root(0, key, key.hashCode) = value) - - def -(key: K) = new HashTrie(root.remove(key, key.hashCode)) - - def iterator = root.elements - - def empty[A]: HashTrie[K, A] = new HashTrie(new EmptyNode[K]) - - def diagnose = root.toString -} - -object HashTrie { - def apply[K, V](pairs: (K, V)*) = pairs.foldLeft(new HashTrie[K, V]) { _ + _ } - - def unapplySeq[K, V](map: HashTrie[K, V]) = map.toSeq -} - -// ============================================================================ -// nodes - -@serializable -private[stm] sealed trait Node[K, +V] { - val size: Int - - def apply(key: K, hash: Int): Option[V] - - def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A] - - def remove(key: K, hash: Int): Node[K, V] - - def elements: Iterator[(K, V)] -} - -@serializable -private[stm] class EmptyNode[K] extends Node[K, Nothing] { - val size = 0 - - def apply(key: K, hash: Int) = None - - def update[V](shift: Int, key: K, hash: Int, value: V) = new LeafNode(key, hash, value) - - def remove(key: K, hash: Int) = this - - lazy val elements = new Iterator[(K, Nothing)] { - val hasNext = false - - val next = null - } -} - -private[stm] abstract class SingleNode[K, +V] extends Node[K, V] { - val hash: Int -} - - -private[stm] class LeafNode[K, +V](key: K, val hash: Int, value: V) extends SingleNode[K, V] { - val size = 1 - - def apply(key: K, hash: Int) = if (this.key == key) Some(value) else None - - def update[A >: V](shift: Int, key: K, hash: Int, value: A) = { - if (this.key == key) { - if (this.value == value) this else new LeafNode(key, hash, value) - } else if (this.hash == hash) { - new CollisionNode(hash, this.key -> this.value, key -> value) - } else { - BitmappedNode(shift)(this, key, hash, value) - } - } - - def remove(key: K, hash: Int) = if (this.key == key) new EmptyNode[K] else this - - def elements = new Iterator[(K, V)] { - var hasNext = true - - def next = { - hasNext = false - (key, value) - } - } - - override def toString = "LeafNode(" + key + " -> " + value + ")" -} - - -private[stm] class CollisionNode[K, +V](val hash: Int, bucket: List[(K, V)]) extends SingleNode[K, V] { - lazy val size = bucket.length - - def this(hash: Int, pairs: (K, V)*) = this(hash, pairs.toList) - - def apply(key: K, hash: Int) = { - for { - (_, v) <- bucket find { case (k, _) => k == key } - } yield v - } - - override def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A] = { - if (this.hash == hash) { - var found = false - - val newBucket = for ((k, v) <- bucket) yield { - if (k == key) { - found = true - (key, value) - } else (k, v) - } - - new CollisionNode(hash, if (found) newBucket else (key, value) :: bucket) - } else { - BitmappedNode(shift)(this, key, hash, value) - } - } - - override def remove(key: K, hash: Int) = { - val newBucket = bucket filter { case (k, _) => k != key } - - if (newBucket.length == bucket.length) this else { - if (newBucket.length == 1) { - val (key, value) = newBucket.head - new LeafNode(key, hash, value) - } else new CollisionNode(hash, newBucket) - } - } - - def iterator = bucket.iterator - - def elements = bucket.iterator - - override def toString = "CollisionNode(" + bucket.toString + ")" -} - -private[stm] class BitmappedNode[K, +V](shift: Int)(table: Array[Node[K, V]], bits: Int) extends Node[K, V] { - lazy val size = { - val sizes = for { - n <- table - if n != null - } yield n.size - - sizes.foldLeft(0) { _ + _ } - } - - def apply(key: K, hash: Int) = { - val i = (hash >>> shift) & 0x01f - val mask = 1 << i - - if ((bits & mask) == mask) table(i)(key, hash) else None - } - - override def update[A >: V](levelShift: Int, key: K, hash: Int, value: A): Node[K, A] = { - val i = (hash >>> shift) & 0x01f - val mask = 1 << i - - if ((bits & mask) == mask) { - val node = (table(i)(shift + 5, key, hash) = value) - - if (node == table(i)) this else { - val newTable = new Array[Node[K, A]](table.length) - Array.copy(table, 0, newTable, 0, table.length) - - newTable(i) = node - - new BitmappedNode(shift)(newTable, bits) - } - } else { - val newTable = new Array[Node[K, A]](math.max(table.length, i + 1)) - Array.copy(table, 0, newTable, 0, table.length) - - newTable(i) = new LeafNode(key, hash, value) - - val newBits = bits | mask - if (newBits == ~0) { - new FullNode(shift)(newTable) - } else { - new BitmappedNode(shift)(newTable, newBits) - } - } - } - - def remove(key: K, hash: Int) = { - val i = (hash >>> shift) & 0x01f - val mask = 1 << i - - if ((bits & mask) == mask) { - val node = table(i).remove(key, hash) - - if (node == table(i)) { - this - } else if (node.isInstanceOf[EmptyNode[_]]) { - if (size == 1) new EmptyNode[K] else { - val adjustedBits = bits ^ mask - val log = math.log(adjustedBits) / math.log(2) - - if (log.toInt.toDouble == log) { // last one - table(log.toInt) - } else { - val newTable = new Array[Node[K, V]](table.length) - Array.copy(table, 0, newTable, 0, newTable.length) - - newTable(i) = null - - new BitmappedNode(shift)(newTable, adjustedBits) - } - } - } else { - val newTable = new Array[Node[K, V]](table.length) - Array.copy(table, 0, newTable, 0, table.length) - - newTable(i) = node - - new BitmappedNode(shift)(newTable, bits) - } - } else this - } - - def elements = { - table.foldLeft(emptyElements) { (it, e) => - if (e eq null) it else it ++ e.elements - } - } - - override def toString = "BitmappedNode(" + size + "," + table.filter(_ ne null).toList.toString + ")" - - private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] { - val hasNext = false - - val next = null - } -} - - -private[stm] object BitmappedNode { - def apply[K, V](shift: Int)(node: SingleNode[K, V], key: K, hash: Int, value: V) = { - val table = new Array[Node[K, V]](math.max((hash >>> shift) & 0x01f, (node.hash >>> shift) & 0x01f) + 1) - - val preBits = { - val i = (node.hash >>> shift) & 0x01f - table(i) = node - 1 << i - } - - val bits = { - val i = (hash >>> shift) & 0x01f - val mask = 1 << i - - if ((preBits & mask) == mask) { - table(i) = (table(i)(shift + 5, key, hash) = value) - } else { - table(i) = new LeafNode(key, hash, value) - } - - preBits | mask - } - - new BitmappedNode(shift)(table, bits) - } -} - - -private[stm] class FullNode[K, +V](shift: Int)(table: Array[Node[K, V]]) extends Node[K, V] { - lazy val size = table.foldLeft(0) { _ + _.size } - - def apply(key: K, hash: Int) = table((hash >>> shift) & 0x01f)(key, hash) - - def update[A >: V](levelShift: Int, key: K, hash: Int, value: A) = { - val i = (hash >>> shift) & 0x01f - - val node = (table(i)(shift + 5, key, hash) = value) - - if (node == table(i)) this else { - val newTable = new Array[Node[K, A]](32) - Array.copy(table, 0, newTable, 0, 32) - - newTable(i) = node - - new FullNode(shift)(newTable) - } - } - - def remove(key: K, hash: Int) = { - val i = (hash >>> shift) & 0x01f - val mask = 1 << i - - val node = table(i).remove(key, hash) - - if (node == table(i)) this else { - val newTable = new Array[Node[K, V]](32) - Array.copy(table, 0, newTable, 0, 32) - - if (node.isInstanceOf[EmptyNode[_]]) { - newTable(i) = null - new BitmappedNode(shift)(newTable, ~mask) - } else { - newTable(i) = node - new FullNode(shift)(newTable) - } - } - } - - def elements = table.foldLeft(emptyElements) { _ ++ _.elements } - - override def toString = "FullNode(" + table.foldLeft("") { _.toString + ", " + _.toString } + ")" - - private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] { - val hasNext = false - - val next = null - } -} diff --git a/akka-core/src/main/scala/stm/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala index def4838b37..9d0c1cb309 100644 --- a/akka-core/src/main/scala/stm/TransactionalMap.scala +++ b/akka-core/src/main/scala/stm/TransactionalMap.scala @@ -4,29 +4,29 @@ package se.scalablesolutions.akka.stm +import scala.collection.immutable.HashMap + import se.scalablesolutions.akka.util.UUID -import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance +import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction object TransactionalMap { def apply[K, V]() = new TransactionalMap[K, V] - def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashTrie(pairs: _*))) + def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashMap(pairs: _*))) } /** - * Implements an in-memory transactional Map based on Clojure's PersistentMap. - * - * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. - * + * TODO: documentation + * * @author Jonas Bonér */ -class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] { +class TransactionalMap[K, V](initialOpt: Option[HashMap[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] { def this() = this(None) // Java compatibility val uuid = UUID.newUuid.toString - protected[this] val ref = new Ref(initialOpt.orElse(Some(new HashTrie[K, V]))) + protected[this] val ref = new Ref(initialOpt.orElse(Some(HashMap[K, V]()))) def -=(key: K) = { remove(key) @@ -52,14 +52,14 @@ class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends override def put(key: K, value: V): Option[V] = { val map = ref.get.get val oldValue = map.get(key) - ref.swap(map.update(key, value)) + ref.swap(map.updated(key, value)) oldValue } override def update(key: K, value: V) = { val map = ref.get.get val oldValue = map.get(key) - ref.swap(map.update(key, value)) + ref.swap(map.updated(key, value)) } def iterator = ref.get.get.iterator @@ -68,7 +68,7 @@ class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends override def contains(key: K): Boolean = ref.get.get.contains(key) - override def clear = ref.swap(new HashTrie[K, V]) + override def clear = ref.swap(HashMap[K, V]()) override def size: Int = ref.get.get.size @@ -80,6 +80,5 @@ class TransactionalMap[K, V](initialOpt: Option[HashTrie[K, V]] = None) extends override def toString = if (outsideTransaction) "" else super.toString - def outsideTransaction = - org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null + def outsideTransaction = getThreadLocalTransaction eq null } diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala index 5c219f7a7f..9153500cb7 100644 --- a/akka-core/src/main/scala/stm/TransactionalVector.scala +++ b/akka-core/src/main/scala/stm/TransactionalVector.scala @@ -4,9 +4,11 @@ package se.scalablesolutions.akka.stm +import scala.collection.immutable.Vector + import se.scalablesolutions.akka.util.UUID -import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance +import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction object TransactionalVector { def apply[T]() = new TransactionalVector[T] @@ -15,10 +17,8 @@ object TransactionalVector { } /** - * Implements an in-memory transactional Vector based on Clojure's PersistentVector. - * - * Not thread-safe, but should only be using from within an Actor, e.g. one single thread at a time. - * + * TODO: documentation + * * @author Jonas Bonér */ class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] { @@ -26,22 +26,22 @@ class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Trans val uuid = UUID.newUuid.toString - private[this] val ref = new Ref(initialOpt.orElse(Some(EmptyVector))) + private[this] val ref = new Ref(initialOpt.orElse(Some(Vector[T]()))) - def clear = ref.swap(EmptyVector) + def clear = ref.swap(Vector[T]()) def +(elem: T) = add(elem) - def add(elem: T) = ref.swap(ref.get.get + elem) + def add(elem: T) = ref.swap(ref.get.get :+ elem) def get(index: Int): T = ref.get.get.apply(index) /** * Removes the tail element of this vector. */ - def pop = ref.swap(ref.get.get.pop) + def pop = ref.swap(ref.get.get.dropRight(1)) - def update(index: Int, elem: T) = ref.swap(ref.get.get.update(index, elem)) + def update(index: Int, elem: T) = ref.swap(ref.get.get.updated(index, elem)) def length: Int = ref.get.get.length @@ -58,3 +58,4 @@ class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Trans def outsideTransaction = org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction eq null } + diff --git a/akka-core/src/main/scala/stm/Vector.scala b/akka-core/src/main/scala/stm/Vector.scala deleted file mode 100644 index 7d524cd2a8..0000000000 --- a/akka-core/src/main/scala/stm/Vector.scala +++ /dev/null @@ -1,353 +0,0 @@ -/** - Copyright (c) 2007-2008, Rich Hickey - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Clojure nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - **/ - -package se.scalablesolutions.akka.stm - -import Vector._ - -/** - * A straight port of Clojure's PersistentVector class. - * - * @author Daniel Spiewak - * @author Rich Hickey - */ -@serializable -class Vector[+T] private (val length: Int, shift: Int, root: Array[AnyRef], tail: Array[AnyRef]) - extends IndexedSeq[T] with PersistentDataStructure { outer => - private val tailOff = length - tail.length - - /* - * The design of this data structure inherantly requires heterogenous arrays. - * It is *possible* to design around this, but the result is comparatively - * quite inefficient. With respect to this fact, I have left the original - * (somewhat dynamically-typed) implementation in place. - */ - - private[stm] def this() = this(0, 5, EmptyArray, EmptyArray) - - def apply(i: Int): T = { - if (i >= 0 && i < length) { - if (i >= tailOff) { - tail(i & 0x01f).asInstanceOf[T] - } else { - var arr = root - var level = shift - - while (level > 0) { - arr = arr((i >>> level) & 0x01f).asInstanceOf[Array[AnyRef]] - level -= 5 - } - - arr(i & 0x01f).asInstanceOf[T] - } - } else throw new IndexOutOfBoundsException(i.toString) - } - - def update[A >: T](i: Int, obj: A): Vector[A] = { - if (i >= 0 && i < length) { - if (i >= tailOff) { - val newTail = new Array[AnyRef](tail.length) - Array.copy(tail, 0, newTail, 0, tail.length) - newTail(i & 0x01f) = obj.asInstanceOf[AnyRef] - - new Vector[A](length, shift, root, newTail) - } else { - new Vector[A](length, shift, doAssoc(shift, root, i, obj), tail) - } - } else if (i == length) { - this + obj - } else throw new IndexOutOfBoundsException(i.toString) - } - - private def doAssoc[A >: T](level: Int, arr: Array[AnyRef], i: Int, obj: A): Array[AnyRef] = { - val ret = new Array[AnyRef](arr.length) - Array.copy(arr, 0, ret, 0, arr.length) - - if (level == 0) { - ret(i & 0x01f) = obj.asInstanceOf[AnyRef] - } else { - val subidx = (i >>> level) & 0x01f - ret(subidx) = doAssoc(level - 5, arr(subidx).asInstanceOf[Array[AnyRef]], i, obj) - } - - ret - } - - def ++[A >: T](other: Iterable[A]) = other.foldLeft(this:Vector[A]) { _ + _ } - - def +[A >: T](obj: A): Vector[A] = { - if (tail.length < 32) { - val newTail = new Array[AnyRef](tail.length + 1) - Array.copy(tail, 0, newTail, 0, tail.length) - newTail(tail.length) = obj.asInstanceOf[AnyRef] - - new Vector[A](length + 1, shift, root, newTail) - } else { - var (newRoot, expansion) = pushTail(shift - 5, root, tail, null) - var newShift = shift - - if (expansion ne null) { - newRoot = array(newRoot, expansion) - newShift += 5 - } - - new Vector[A](length + 1, newShift, newRoot, array(obj.asInstanceOf[AnyRef])) - } - } - - private def pushTail(level: Int, arr: Array[AnyRef], tailNode: Array[AnyRef], expansion: AnyRef): (Array[AnyRef], AnyRef) = { - val newChild = if (level == 0) tailNode else { - val (newChild, subExpansion) = pushTail(level - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], tailNode, expansion) - - if (subExpansion eq null) { - val ret = new Array[AnyRef](arr.length) - Array.copy(arr, 0, ret, 0, arr.length) - - ret(arr.length - 1) = newChild - - return (ret, null) - } else subExpansion - } - - // expansion - if (arr.length == 32) { - (arr, array(newChild)) - } else { - val ret = new Array[AnyRef](arr.length + 1) - Array.copy(arr, 0, ret, 0, arr.length) - ret(arr.length) = newChild - - (ret, null) - } - } - - /** - * Removes the tail element of this vector. - */ - def pop: Vector[T] = { - if (length == 0) { - throw new IllegalStateException("Can't pop empty vector") - } else if (length == 1) { - EmptyVector - } else if (tail.length > 1) { - val newTail = new Array[AnyRef](tail.length - 1) - Array.copy(tail, 0, newTail, 0, newTail.length) - - new Vector[T](length - 1, shift, root, newTail) - } else { - var (newRoot, pTail) = popTail(shift - 5, root, null) - var newShift = shift - - if (newRoot eq null) { - newRoot = EmptyArray - } - - if (shift > 5 && newRoot.length == 1) { - newRoot = newRoot(0).asInstanceOf[Array[AnyRef]] - newShift -= 5 - } - - new Vector[T](length - 1, newShift, newRoot, pTail.asInstanceOf[Array[AnyRef]]) - } - } - - private def popTail(shift: Int, arr: Array[AnyRef], pTail: AnyRef): (Array[AnyRef], AnyRef) = { - val newPTail = if (shift > 0) { - val (newChild, subPTail) = popTail(shift - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], pTail) - - if (newChild ne null) { - val ret = new Array[AnyRef](arr.length) - Array.copy(arr, 0, ret, 0, arr.length) - - ret(arr.length - 1) = newChild - - return (ret, subPTail) - } - subPTail - } else if (shift == 0) { - arr(arr.length - 1) - } else pTail - - // contraction - if (arr.length == 1) { - (null, newPTail) - } else { - val ret = new Array[AnyRef](arr.length - 1) - Array.copy(arr, 0, ret, 0, ret.length) - - (ret, newPTail) - } - } - - override def filter(p: (T)=>Boolean) = { - var back = new Vector[T] - var i = 0 - - while (i < length) { - val e = apply(i) - if (p(e)) back += e - - i += 1 - } - - back - } - - def flatMap[A](f: (T)=>Iterable[A]): Vector[A] = { - var back = new Vector[A] - var i = 0 - - while (i < length) { - f(apply(i)) foreach { back += _ } - i += 1 - } - - back - } - - def map[A](f: (T)=>A): Vector[A] = { - var back = new Vector[A] - var i = 0 - - while (i < length) { - back += f(apply(i)) - i += 1 - } - - back - } - - override def reverse: Vector[T] = new VectorProjection[T] { - override val length = outer.length - - override def apply(i: Int) = outer.apply(length - i - 1) - } - - def subseq(from: Int, end: Int) = subVector(from, end) - - def subVector(from: Int, end: Int): Vector[T] = { - if (from < 0) { - throw new IndexOutOfBoundsException(from.toString) - } else if (end >= length) { - throw new IndexOutOfBoundsException(end.toString) - } else if (end <= from) { - throw new IllegalArgumentException("Invalid range: " + from + ".." + end) - } else { - new VectorProjection[T] { - override val length = end - from - - override def apply(i: Int) = outer.apply(i + from) - } - } - } - - def zip[A](that: Vector[A]) = { - var back = new Vector[(T, A)] - var i = 0 - - val limit = math.min(length, that.length) - while (i < limit) { - back += (apply(i), that(i)) - i += 1 - } - - back - } - - def zipWithIndex = { - var back = new Vector[(T, Int)] - var i = 0 - - while (i < length) { - back += (apply(i), i) - i += 1 - } - - back - } - - override def equals(other: Any) = other match { - case vec: Vector[_] => { - var back = length == vec.length - var i = 0 - - while (i < length) { - back &&= apply(i) == vec.apply(i) - i += 1 - } - - back - } - - case _ => false - } - - override def hashCode = foldLeft(0) { _ ^ _.hashCode } -} - -object Vector { - private[stm] val EmptyArray = new Array[AnyRef](0) - - def apply[T](elems: T*) = elems.foldLeft(EmptyVector:Vector[T]) { _ + _ } - - def unapplySeq[T](vec: Vector[T]): Option[Seq[T]] = Some(vec) - - @inline - private[stm] def array(elems: AnyRef*) = { - val back = new Array[AnyRef](elems.length) - Array.copy(elems.toArray, 0, back, 0, back.length) - - back - } -} - -object EmptyVector extends Vector[Nothing] - -private[stm] abstract class VectorProjection[+T] extends Vector[T] { - override val length: Int - override def apply(i: Int): T - - override def +[A >: T](e: A) = innerCopy + e - - override def update[A >: T](i: Int, e: A) = { - if (i < 0) { - throw new IndexOutOfBoundsException(i.toString) - } else if (i > length) { - throw new IndexOutOfBoundsException(i.toString) - } else innerCopy(i) = e - } - - private lazy val innerCopy = foldLeft(EmptyVector:Vector[T]) { _ + _ } -} - diff --git a/akka-core/src/test/scala/VectorBugTestSuite.scala b/akka-core/src/test/scala/VectorBugTestSuite.scala deleted file mode 100644 index 658ace3681..0000000000 --- a/akka-core/src/test/scala/VectorBugTestSuite.scala +++ /dev/null @@ -1,17 +0,0 @@ -package se.scalablesolutions.akka.stm - -import org.scalatest.FunSuite -import Transaction.Global._ - -class TransactionalVectorBugTestSuite extends FunSuite { - - test("adding more than 32 items to a Vector shouldn't blow it up") { - atomic { - var v1 = new Vector[Int]() - for (i <- 0 to 31) { - v1 = v1 + i - } - v1 = v1 + 32 - } - } -} From 21a6021d26567adaa40faceda41db90fc9e6ed59 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 13:53:33 +1200 Subject: [PATCH 11/38] Updated stm tests --- .../test/scala/{TransactionalRefSpec.scala => RefSpec.scala} | 4 ++-- akka-core/src/test/scala/StmSpec.scala | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) rename akka-core/src/test/scala/{TransactionalRefSpec.scala => RefSpec.scala} (96%) diff --git a/akka-core/src/test/scala/TransactionalRefSpec.scala b/akka-core/src/test/scala/RefSpec.scala similarity index 96% rename from akka-core/src/test/scala/TransactionalRefSpec.scala rename to akka-core/src/test/scala/RefSpec.scala index d21f2af872..f30f008619 100644 --- a/akka-core/src/test/scala/TransactionalRefSpec.scala +++ b/akka-core/src/test/scala/RefSpec.scala @@ -8,9 +8,9 @@ import org.junit.runner.RunWith import se.scalablesolutions.akka.actor.Actor._ @RunWith(classOf[JUnitRunner]) -class TransactionalRefSpec extends Spec with ShouldMatchers { +class RefSpec extends Spec with ShouldMatchers { - describe("A TransactionalRef") { + describe("A Ref") { import Transaction.Local._ it("should optionally accept an initial value") { diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala index 4390088185..fb560f2ac4 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/StmSpec.scala @@ -1,7 +1,6 @@ -package se.scalablesolutions.akka.actor - -import se.scalablesolutions.akka.stm._ +package se.scalablesolutions.akka.stm +import se.scalablesolutions.akka.actor.{Actor, Transactor} import Actor._ import org.scalatest.Spec From 8a46c5ae8c8eb62aa59a72edb598c0049caf8363 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 14:16:47 +1200 Subject: [PATCH 12/38] Added Duration utility class for working with j.u.c.TimeUnit --- akka-core/src/main/scala/util/Duration.scala | 93 ++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 akka-core/src/main/scala/util/Duration.scala diff --git a/akka-core/src/main/scala/util/Duration.scala b/akka-core/src/main/scala/util/Duration.scala new file mode 100644 index 0000000000..c2e08f68b4 --- /dev/null +++ b/akka-core/src/main/scala/util/Duration.scala @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.util + +import java.util.concurrent.TimeUnit + +/** + * Utility for working with java.util.concurrent.TimeUnit durations. + *

+ * Example: + *

+ * import se.scalablesolutions.akka.util.Duration
+ * import java.util.concurrent.TimeUnit
+ * 
+ * val duration = Duration(100, TimeUnit.MILLISECONDS)
+ * val duration = Duration(100, "millis")
+ * 
+ * duration.toNanos
+ * 
+ *

+ * Implicits are also provided for Int and Long. Example usage: + *

+ * import se.scalablesolutions.akka.util.duration._
+ *
+ * val duration = 100.millis
+ * 
+ */ +object Duration { + def apply(length: Long, unit: TimeUnit) = new Duration(length, unit) + def apply(length: Long, unit: String) = new Duration(length, timeUnit(unit)) + + def timeUnit(unit: String) = unit.toLowerCase match { + case "nanoseconds" | "nanos" | "nanosecond" | "nano" => TimeUnit.NANOSECONDS + case "microseconds" | "micros" | "microsecond" | "micro" => TimeUnit.MICROSECONDS + case "milliseconds" | "millis" | "millisecond" | "milli" => TimeUnit.MILLISECONDS + case _ => TimeUnit.SECONDS + } +} + +class Duration(val length: Long, val unit: TimeUnit) { + def toNanos = unit.toNanos(length) + def toMicros = unit.toMicros(length) + def toMillis = unit.toMillis(length) + def toSeconds = unit.toSeconds(length) + override def toString = "Duration(" + length + ", " + unit + ")" +} + +package object duration { + implicit def intToDurationInt(n: Int) = new DurationInt(n) + implicit def longToDurationLong(n: Long) = new DurationLong(n) +} + +class DurationInt(n: Int) { + def nanoseconds = Duration(n, TimeUnit.NANOSECONDS) + def nanos = Duration(n, TimeUnit.NANOSECONDS) + def nanosecond = Duration(n, TimeUnit.NANOSECONDS) + def nano = Duration(n, TimeUnit.NANOSECONDS) + + def microseconds = Duration(n, TimeUnit.MICROSECONDS) + def micros = Duration(n, TimeUnit.MICROSECONDS) + def microsecond = Duration(n, TimeUnit.MICROSECONDS) + def micro = Duration(n, TimeUnit.MICROSECONDS) + + def milliseconds = Duration(n, TimeUnit.MILLISECONDS) + def millis = Duration(n, TimeUnit.MILLISECONDS) + def millisecond = Duration(n, TimeUnit.MILLISECONDS) + def milli = Duration(n, TimeUnit.MILLISECONDS) + + def seconds = Duration(n, TimeUnit.SECONDS) + def second = Duration(n, TimeUnit.SECONDS) +} + +class DurationLong(n: Long) { + def nanoseconds = Duration(n, TimeUnit.NANOSECONDS) + def nanos = Duration(n, TimeUnit.NANOSECONDS) + def nanosecond = Duration(n, TimeUnit.NANOSECONDS) + def nano = Duration(n, TimeUnit.NANOSECONDS) + + def microseconds = Duration(n, TimeUnit.MICROSECONDS) + def micros = Duration(n, TimeUnit.MICROSECONDS) + def microsecond = Duration(n, TimeUnit.MICROSECONDS) + def micro = Duration(n, TimeUnit.MICROSECONDS) + + def milliseconds = Duration(n, TimeUnit.MILLISECONDS) + def millis = Duration(n, TimeUnit.MILLISECONDS) + def millisecond = Duration(n, TimeUnit.MILLISECONDS) + def milli = Duration(n, TimeUnit.MILLISECONDS) + + def seconds = Duration(n, TimeUnit.SECONDS) + def second = Duration(n, TimeUnit.SECONDS) +} From bb93d72d02843ea29f74050db905af16772e019d Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 14:20:52 +1200 Subject: [PATCH 13/38] Removed some unused stm config options --- akka-core/src/main/scala/stm/TransactionManagement.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index a6ec288466..dd9a4e28df 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -22,10 +22,7 @@ object TransactionManagement extends TransactionManagement { val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", true)) val FAIR_TRANSACTIONS = config.getBool("akka.stm.fair", true) - val INTERRUPTIBLE = config.getBool("akka.stm.interruptible", true) - val MAX_NR_OF_RETRIES = config.getInt("akka.stm.max-nr-of-retries", 1000) - val TRANSACTION_TIMEOUT = config.getInt("akka.stm.timeout", 10000) - val SMART_TX_LENGTH_SELECTOR = config.getBool("akka.stm.smart-tx-length-selector", true) + def isTransactionalityEnabled = TRANSACTION_ENABLED.get def disableTransactions = TRANSACTION_ENABLED.set(false) From f9e52b5e0d07b0fbe1b72e2e08da56bd9e129176 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 14:22:36 +1200 Subject: [PATCH 14/38] Removed unused stm config options from akka conf --- config/akka-reference.conf | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 2ac6bf42f1..f7f9f0abc5 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -33,8 +33,6 @@ service = on fair = on # should transactions be fair or non-fair (non fair yield better performance) - max-nr-of-retries = 1000 # max nr of retries of a failing transaction before giving up - timeout = 10000 # transaction timeout; if transaction has not committed within the timeout then it is aborted jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will # begin (or join), commit or rollback the JTA transaction. Default is 'off'. From cb241a1dfc4720e32d43572b01a51ef06cfd6960 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 14:44:57 +1200 Subject: [PATCH 15/38] Added Transaction.Util with methods for transaction lifecycle and blocking --- .../src/main/scala/stm/Transaction.scala | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 7c1e9010a8..6f992d0aee 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -22,6 +22,7 @@ import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.templates.{TransactionTemplate, OrElseTemplate} import org.multiverse.api.backoff.ExponentialBackoffPolicy import org.multiverse.stms.alpha.AlphaStm +import org.multiverse.api.StmUtils class NoTransactionInScopeException extends RuntimeException class TransactionRetryException(message: String) extends RuntimeException(message) @@ -45,18 +46,6 @@ object Transaction { * } * * - * Example of atomically-orElse transaction management. - * Which is a good way to reduce contention and transaction collisions. - *
-   * import se.scalablesolutions.akka.stm.Transaction.Local._
-   *
-   * atomically  {
-   *   .. // try to do something
-   * } orElse  {
-   *   .. // if transaction clashes try do do something else to minimize contention
-   * }
-   * 
- * * @author Jonas Bonér */ object Local extends TransactionManagement with Logging { @@ -72,22 +61,6 @@ object Transaction { } }.execute() } - - /** - * See ScalaDoc on Transaction.Local class. - */ - def atomically[A](firstBody: => A) = elseBody(firstBody) - - /** - * Should only be used together with atomically to form atomically-orElse constructs. - * See ScalaDoc on class. - */ - def elseBody[A](firstBody: => A) = new { - def orElse(secondBody: => A) = new OrElseTemplate[A] { - def either(t: MultiverseTransaction) = firstBody - def orelse(t: MultiverseTransaction) = secondBody - }.execute() - } } /** @@ -130,6 +103,25 @@ object Transaction { } } + /** + * TODO: document + */ + object Util { + + def deferred[T](body: => T): Unit = StmUtils.scheduleDeferredTask(new Runnable { def run = body }) + + def compensating[T](body: => T): Unit = StmUtils.scheduleCompensatingTask(new Runnable { def run = body }) + + def retry = StmUtils.retry + + def either[A](firstBody: => A) = new { + def orElse(secondBody: => A) = new OrElseTemplate[A] { + def either(t: MultiverseTransaction) = firstBody + def orelse(t: MultiverseTransaction) = secondBody + }.execute() + } + } + /** * Attach an Akka-specific Transaction to the current Multiverse transaction. * Must be called within a Multiverse transaction. From 9c026ad27d1a94e0578aa105aa6433b81c7a4bcf Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 15:36:11 +1200 Subject: [PATCH 16/38] Fixed import in ants sample for removed Vector class --- akka-samples/akka-sample-ants/src/main/scala/Ants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala index bac99f58c6..ccfdf21581 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -9,7 +9,7 @@ import scala.util.Random.{nextInt => randomInt} import se.scalablesolutions.akka import akka.actor.{ActorRef, Transactor, Scheduler} import akka.actor.Actor.actorOf -import akka.stm.{Vector => _, _} +import akka.stm._ import akka.stm.Transaction.Local._ object Config { From ef4f525b52033304dda02c98846e2d9e590c0d77 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Thu, 10 Jun 2010 15:49:26 +1200 Subject: [PATCH 17/38] Configurable TransactionFactory --- .../src/main/scala/stm/Transaction.scala | 59 ++++---- .../main/scala/stm/TransactionFactory.scala | 128 ++++++++++++++++++ akka-core/src/test/scala/RefSpec.scala | 2 +- akka-core/src/test/scala/StmSpec.scala | 2 +- 4 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 akka-core/src/main/scala/stm/TransactionFactory.scala diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala index 6f992d0aee..5194098799 100644 --- a/akka-core/src/main/scala/stm/Transaction.scala +++ b/akka-core/src/main/scala/stm/Transaction.scala @@ -6,7 +6,6 @@ package se.scalablesolutions.akka.stm import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.TimeUnit import javax.transaction.{TransactionManager, UserTransaction, Status, TransactionSynchronizationRegistry} @@ -19,9 +18,8 @@ import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.api.lifecycle.{TransactionLifecycleListener, TransactionLifecycleEvent} import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance import org.multiverse.api.ThreadLocalTransaction._ -import org.multiverse.templates.{TransactionTemplate, OrElseTemplate} -import org.multiverse.api.backoff.ExponentialBackoffPolicy -import org.multiverse.stms.alpha.AlphaStm +import org.multiverse.templates.{TransactionalCallable, OrElseTemplate} +import org.multiverse.api.{TraceLevel => MultiverseTraceLevel} import org.multiverse.api.StmUtils class NoTransactionInScopeException extends RuntimeException @@ -50,16 +48,18 @@ object Transaction { */ object Local extends TransactionManagement with Logging { - /** - * See ScalaDoc on Transaction.Local class. - */ - def atomic[T](body: => T): T = { - new TransactionTemplate[T]() { - def execute(mtx: MultiverseTransaction): T = { - Transaction.attach + object DefaultLocalTransactionConfig extends TransactionConfig + object DefaultLocalTransactionFactory extends TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction") + + 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 body } - }.execute() + }) } } @@ -68,8 +68,7 @@ object Transaction { * You have to use these if you do need to have one transaction span multiple threads (or Actors). *

* Example of atomic transaction management using the atomic block. - *

- * Here are some examples (assuming implicit transaction family name in scope): + *

: *

    * import se.scalablesolutions.akka.stm.Transaction.Global._
    *
@@ -82,15 +81,16 @@ object Transaction {
    */
   object Global extends TransactionManagement with Logging {
 
-    /**
-     * See ScalaDoc on Transaction.Global class.
-     */
-    def atomic[T](body: => T): T = {
-      var isTopLevelTransaction = false
-      new TransactionTemplate[T]() {
-        def execute(mtx: MultiverseTransaction): T = {
+    object DefaultGlobalTransactionConfig extends TransactionConfig
+    object DefaultGlobalTransactionFactory extends TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
+
+    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]() {
+        def call(mtx: MultiverseTransaction): T = {
           if (!isTransactionSetInScope) createNewTransactionSet
-          Transaction.attach
+          factory.addHooks
           val result = body
           val txSet = getTransactionSetInScope
           log.trace("Committing transaction [%s]\n\tby joining transaction set [%s]", mtx, txSet)
@@ -99,7 +99,7 @@ object Transaction {
           clearTransaction
           result
         }
-      }.execute()
+      })
     }
   }
 
@@ -124,7 +124,7 @@ object Transaction {
 
   /**
    * Attach an Akka-specific Transaction to the current Multiverse transaction.
-   * Must be called within a Multiverse transaction.
+   * Must be called within a Multiverse transaction. Used by TransactionFactory.addHooks
    */ 
   private[akka] def attach = {
     val mtx = getRequiredThreadLocalTransaction
@@ -140,6 +140,16 @@ object Transaction {
       }
     })
   }
+
+  /**
+   * Mapping to Multiverse TraceLevel.
+   */ 
+  object TraceLevel {
+    val None = MultiverseTraceLevel.none
+    val Coarse = MultiverseTraceLevel.course // mispelling?
+    val Course = MultiverseTraceLevel.course
+    val Fine = MultiverseTraceLevel.fine    
+  }
 }
 
 /**
@@ -278,3 +288,4 @@ trait Committable {
 trait Abortable {
   def abort: Unit
 }
+
diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala
new file mode 100644
index 0000000000..9147e3ce36
--- /dev/null
+++ b/akka-core/src/main/scala/stm/TransactionFactory.scala
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB 
+ */
+
+package se.scalablesolutions.akka.stm
+
+import se.scalablesolutions.akka.config.Config._
+import se.scalablesolutions.akka.util.Duration
+
+import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance
+import org.multiverse.stms.alpha.AlphaStm
+import org.multiverse.templates.TransactionBoilerplate
+import org.multiverse.api.TraceLevel
+
+/**
+ * For configuring multiverse transactions.
+ */ 
+object TransactionConfig {
+  val FAMILY_NAME      = "DefaultTransaction"
+  val READONLY         = false
+  val MAX_RETRIES      = config.getInt("akka.stm.max-retries", 1000)
+  val TIMEOUT          = config.getLong("akka.stm.timeout", Long.MaxValue)
+  val TIME_UNIT        = config.getString("akka.stm.time-unit", "seconds")
+  val TRACK_READS      = config.getBool("akka.stm.track-reads", false)
+  val WRITE_SKEW       = config.getBool("akka.stm.write-skew", true)
+  val EXPLICIT_RETRIES = config.getBool("akka.stm.explicit-retries", false)
+  val INTERRUPTIBLE    = config.getBool("akka.stm.interruptible", false)
+  val SPECULATIVE      = config.getBool("akka.stm.speculative", false)
+  val QUICK_RELEASE    = config.getBool("akka.stm.quick-release", true)
+  val TRACE_LEVEL      = traceLevel(config.getString("akka.stm.trace-level", "none"))
+  val HOOKS            = config.getBool("akka.stm.hooks", true)
+
+  val DefaultTimeout = Duration(TIMEOUT, TIME_UNIT)
+
+  def traceLevel(level: String) = level.toLowerCase match {
+    case "coarse" | "course" => Transaction.TraceLevel.Coarse
+    case "fine" => Transaction.TraceLevel.Fine
+    case _ => Transaction.TraceLevel.None
+  }
+
+  def apply(familyName: String       = FAMILY_NAME,
+            readonly: Boolean        = READONLY,
+            maxRetries: Int          = MAX_RETRIES,
+            timeout: Duration        = DefaultTimeout,
+            trackReads: Boolean      = TRACK_READS,
+            writeSkew: Boolean       = WRITE_SKEW,
+            explicitRetries: Boolean = EXPLICIT_RETRIES,
+            interruptible: Boolean   = INTERRUPTIBLE,
+            speculative: Boolean     = SPECULATIVE,
+            quickRelease: Boolean    = QUICK_RELEASE,
+            traceLevel: TraceLevel   = TRACE_LEVEL,
+            hooks: Boolean           = HOOKS) = {
+    new TransactionConfig(familyName, readonly, maxRetries, timeout, trackReads, writeSkew,
+                          explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks)
+  }
+}
+
+/**
+ * For configuring multiverse transactions.
+ */
+class TransactionConfig(val familyName: String       = TransactionConfig.FAMILY_NAME,
+                        val readonly: Boolean        = TransactionConfig.READONLY,
+                        val maxRetries: Int          = TransactionConfig.MAX_RETRIES,
+                        val timeout: Duration        = TransactionConfig.DefaultTimeout,
+                        val trackReads: Boolean      = TransactionConfig.TRACK_READS,
+                        val writeSkew: Boolean       = TransactionConfig.WRITE_SKEW,
+                        val explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES,
+                        val interruptible: Boolean   = TransactionConfig.INTERRUPTIBLE,
+                        val speculative: Boolean     = TransactionConfig.SPECULATIVE,
+                        val quickRelease: Boolean    = TransactionConfig.QUICK_RELEASE,
+                        val traceLevel: TraceLevel   = TransactionConfig.TRACE_LEVEL,
+                        val hooks: Boolean           = TransactionConfig.HOOKS) 
+
+object DefaultTransactionConfig extends TransactionConfig
+
+/**
+ * Wrapper for transaction config, factory, and boilerplate. Used by atomic.
+ */ 
+object TransactionFactory {
+  def apply(config: TransactionConfig) = new TransactionFactory(config)
+
+  def apply(config: TransactionConfig, defaultName: String) = new TransactionFactory(config, defaultName)
+
+  def apply(familyName: String       = TransactionConfig.FAMILY_NAME,
+            readonly: Boolean        = TransactionConfig.READONLY,
+            maxRetries: Int          = TransactionConfig.MAX_RETRIES,
+            timeout: Duration        = TransactionConfig.DefaultTimeout,
+            trackReads: Boolean      = TransactionConfig.TRACK_READS,
+            writeSkew: Boolean       = TransactionConfig.WRITE_SKEW,
+            explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES,
+            interruptible: Boolean   = TransactionConfig.INTERRUPTIBLE,
+            speculative: Boolean     = TransactionConfig.SPECULATIVE,
+            quickRelease: Boolean    = TransactionConfig.QUICK_RELEASE,
+            traceLevel: TraceLevel   = TransactionConfig.TRACE_LEVEL,
+            hooks: Boolean           = TransactionConfig.HOOKS) = {
+    val config = new TransactionConfig(familyName, readonly, maxRetries, timeout, trackReads, writeSkew,
+                                        explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks)
+    new TransactionFactory(config)
+  }
+}
+
+/**
+ * Wrapper for transaction config, factory, and boilerplate. Used by atomic.
+ */ 
+class TransactionFactory(val config: TransactionConfig = DefaultTransactionConfig, defaultName: String = TransactionConfig.FAMILY_NAME) {
+  self =>
+
+  // use the config family name if it's been set, otherwise defaultName - used by actors to set class name as default
+  val familyName = if (config.familyName != TransactionConfig.FAMILY_NAME) config.familyName else defaultName
+
+  val factory = (getGlobalStmInstance().asInstanceOf[AlphaStm].getTransactionFactoryBuilder()
+                 .setFamilyName(familyName)
+                 .setReadonly(config.readonly)
+                 .setMaxRetries(config.maxRetries)
+                 .setTimeoutNs(config.timeout.toNanos)
+                 .setReadTrackingEnabled(config.trackReads)
+                 .setWriteSkewAllowed(config.writeSkew)
+                 .setExplicitRetryAllowed(config.explicitRetries)
+                 .setInterruptible(config.interruptible)
+                 .setSpeculativeConfigurationEnabled(config.speculative)
+                 .setQuickReleaseEnabled(config.quickRelease)
+                 .setTraceLevel(config.traceLevel)
+                 .build())
+
+  val boilerplate = new TransactionBoilerplate(factory)
+
+  def addHooks = if (config.hooks) Transaction.attach
+}
diff --git a/akka-core/src/test/scala/RefSpec.scala b/akka-core/src/test/scala/RefSpec.scala
index f30f008619..3b1a1fe3ae 100644
--- a/akka-core/src/test/scala/RefSpec.scala
+++ b/akka-core/src/test/scala/RefSpec.scala
@@ -29,7 +29,7 @@ class RefSpec extends Spec with ShouldMatchers {
       val ref = Ref(3)
 
       try {
-        atomic {
+        atomic(DefaultLocalTransactionFactory) {
           ref.swap(5)
           throw new Exception
         }
diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala
index fb560f2ac4..f08116f4af 100644
--- a/akka-core/src/test/scala/StmSpec.scala
+++ b/akka-core/src/test/scala/StmSpec.scala
@@ -70,7 +70,7 @@ class StmSpec extends
         ref.get.getOrElse(0)
       }
       try {
-        atomic {
+        atomic(DefaultLocalTransactionFactory) {
           increment
           increment
           throw new Exception

From 35743ee78c7c1a2f1b56ea3fa728ffa0c8acb6de Mon Sep 17 00:00:00 2001
From: Peter Vlugter 
Date: Thu, 10 Jun 2010 16:01:23 +1200
Subject: [PATCH 18/38] Updated actor ref to use transaction factory

---
 akka-core/src/main/scala/actor/ActorRef.scala | 46 ++++++++++++++++++-
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala
index b9bcd08938..5413f4fc2a 100644
--- a/akka-core/src/main/scala/actor/ActorRef.scala
+++ b/akka-core/src/main/scala/actor/ActorRef.scala
@@ -9,6 +9,7 @@ import se.scalablesolutions.akka.config.Config.config
 import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy}
 import se.scalablesolutions.akka.config.ScalaConfig._
 import se.scalablesolutions.akka.stm.Transaction.Global._
+import se.scalablesolutions.akka.stm.{TransactionConfig, TransactionFactory, TransactionManagement}
 import se.scalablesolutions.akka.stm.TransactionManagement._
 import se.scalablesolutions.akka.stm.TransactionManagement
 import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
@@ -283,7 +284,17 @@ trait ActorRef extends TransactionManagement {
    */
   @volatile protected[akka] var isTransactor = false
 
-  /**v
+  /**
+   * Configuration for TransactionFactory. User overridable.
+   */
+  protected[akka] var _transactionConfig: TransactionConfig = DefaultGlobalTransactionConfig
+
+  /**
+   * TransactionFactory to be used for atomic when isTransactor. Configuration is overridable.
+   */
+  private[akka] var _transactionFactory: Option[TransactionFactory] = None
+
+  /**
    * This lock ensures thread safety in the dispatching: only one message can
    * be dispatched at once on the actor.
    */
@@ -500,6 +511,16 @@ trait ActorRef extends TransactionManagement {
    */
   def makeTransactionRequired: Unit
 
+  /**
+   * Sets the transaction configuration for this actor. Needs to be invoked before the actor is started.
+   */
+  def transactionConfig_=(config: TransactionConfig): Unit
+
+  /**
+   * Get the transaction configuration for this actor.
+   */
+  def transactionConfig: TransactionConfig
+
   /**
    * Returns the home address and port for this actor.
    */
@@ -870,6 +891,20 @@ sealed class LocalActorRef private[akka](
       "Can not make actor transaction required after it has been started")
   }
 
+  /**
+   * Sets the transaction configuration for this actor. Needs to be invoked before the actor is started.
+   */
+  def transactionConfig_=(config: TransactionConfig) = guard.withGuard {
+    if (!isRunning || isBeingRestarted) _transactionConfig = config
+    else throw new ActorInitializationException(
+      "Cannot set transaction configuration for actor after it has been started")
+  }
+
+  /**
+   * Get the transaction configuration for this actor.
+   */
+  def transactionConfig: TransactionConfig = guard.withGuard { _transactionConfig }
+
   /**
    * Set the contact address for this actor. This is used for replying to messages
    * sent asynchronously when no reply channel exists.
@@ -891,6 +926,9 @@ sealed class LocalActorRef private[akka](
     if (!isRunning) {
       dispatcher.register(this)
       dispatcher.start
+      if (isTransactor) {
+        _transactionFactory = Some(TransactionFactory(_transactionConfig, actorClass.getName))
+      }
       _isRunning = true
       if (!isInInitialization) initializeActorInstance
       else runActorInitialization = true
@@ -904,6 +942,7 @@ sealed class LocalActorRef private[akka](
   def stop = guard.withGuard {
     if (isRunning) {
       dispatcher.unregister(this)
+      _transactionFactory = None
       _isRunning = false
       _isShutDown = true
       actor.shutdown
@@ -1212,7 +1251,8 @@ sealed class LocalActorRef private[akka](
 
     try {
       if (isTransactor) {
-        atomic {
+        val txFactory = _transactionFactory.getOrElse(DefaultGlobalTransactionFactory)
+        atomic(txFactory) {
           actor.base(message)
           setTransactionSet(txSet) // restore transaction set to allow atomic block to do commit
         }
@@ -1452,6 +1492,8 @@ private[akka] case class RemoteActorRef private[akka] (
   def dispatcher_=(md: MessageDispatcher): Unit = unsupported
   def dispatcher: MessageDispatcher = unsupported
   def makeTransactionRequired: Unit = unsupported
+  def transactionConfig_=(config: TransactionConfig): Unit = unsupported
+  def transactionConfig: TransactionConfig = unsupported
   def makeRemote(hostname: String, port: Int): Unit = unsupported
   def makeRemote(address: InetSocketAddress): Unit = unsupported
   def homeAddress_=(address: InetSocketAddress): Unit = unsupported

From ba0b5033c687ae9d839768a33a87ded673379e87 Mon Sep 17 00:00:00 2001
From: Peter Vlugter 
Date: Mon, 14 Jun 2010 19:18:45 +1200
Subject: [PATCH 19/38] Removed some trailing whitespace

---
 .../src/main/scala/stm/Transaction.scala      | 22 +++++++++----------
 .../main/scala/stm/TransactionFactory.scala   |  8 +++----
 .../src/main/scala/stm/TransactionalMap.scala |  2 +-
 .../main/scala/stm/TransactionalVector.scala  |  2 +-
 4 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala
index 5194098799..7539c72ae0 100644
--- a/akka-core/src/main/scala/stm/Transaction.scala
+++ b/akka-core/src/main/scala/stm/Transaction.scala
@@ -52,7 +52,7 @@ object Transaction {
     object DefaultLocalTransactionFactory extends TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction")
 
     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 = {
@@ -85,7 +85,7 @@ object Transaction {
     object DefaultGlobalTransactionFactory extends TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
 
     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]() {
         def call(mtx: MultiverseTransaction): T = {
@@ -105,19 +105,19 @@ object Transaction {
 
   /**
    * TODO: document
-   */ 
+   */
   object Util {
-    
+
     def deferred[T](body: => T): Unit = StmUtils.scheduleDeferredTask(new Runnable { def run = body })
 
     def compensating[T](body: => T): Unit = StmUtils.scheduleCompensatingTask(new Runnable { def run = body })
 
     def retry = StmUtils.retry
 
-    def either[A](firstBody: => A) = new {
-      def orElse(secondBody: => A) = new OrElseTemplate[A] {
-        def either(t: MultiverseTransaction) = firstBody
-        def orelse(t: MultiverseTransaction) = secondBody
+    def either[T](firstBody: => T) = new {
+      def orElse(secondBody: => T) = new OrElseTemplate[T] {
+        def either(mtx: MultiverseTransaction) = firstBody
+        def orelse(mtx: MultiverseTransaction) = secondBody
       }.execute()
     }
   }
@@ -125,7 +125,7 @@ object Transaction {
   /**
    * Attach an Akka-specific Transaction to the current Multiverse transaction.
    * Must be called within a Multiverse transaction. Used by TransactionFactory.addHooks
-   */ 
+   */
   private[akka] def attach = {
     val mtx = getRequiredThreadLocalTransaction
     val tx = new Transaction
@@ -143,12 +143,12 @@ object Transaction {
 
   /**
    * Mapping to Multiverse TraceLevel.
-   */ 
+   */
   object TraceLevel {
     val None = MultiverseTraceLevel.none
     val Coarse = MultiverseTraceLevel.course // mispelling?
     val Course = MultiverseTraceLevel.course
-    val Fine = MultiverseTraceLevel.fine    
+    val Fine = MultiverseTraceLevel.fine
   }
 }
 
diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala
index 9147e3ce36..2269e0edf7 100644
--- a/akka-core/src/main/scala/stm/TransactionFactory.scala
+++ b/akka-core/src/main/scala/stm/TransactionFactory.scala
@@ -14,7 +14,7 @@ import org.multiverse.api.TraceLevel
 
 /**
  * For configuring multiverse transactions.
- */ 
+ */
 object TransactionConfig {
   val FAMILY_NAME      = "DefaultTransaction"
   val READONLY         = false
@@ -69,13 +69,13 @@ class TransactionConfig(val familyName: String       = TransactionConfig.FAMILY_
                         val speculative: Boolean     = TransactionConfig.SPECULATIVE,
                         val quickRelease: Boolean    = TransactionConfig.QUICK_RELEASE,
                         val traceLevel: TraceLevel   = TransactionConfig.TRACE_LEVEL,
-                        val hooks: Boolean           = TransactionConfig.HOOKS) 
+                        val hooks: Boolean           = TransactionConfig.HOOKS)
 
 object DefaultTransactionConfig extends TransactionConfig
 
 /**
  * Wrapper for transaction config, factory, and boilerplate. Used by atomic.
- */ 
+ */
 object TransactionFactory {
   def apply(config: TransactionConfig) = new TransactionFactory(config)
 
@@ -101,7 +101,7 @@ object TransactionFactory {
 
 /**
  * Wrapper for transaction config, factory, and boilerplate. Used by atomic.
- */ 
+ */
 class TransactionFactory(val config: TransactionConfig = DefaultTransactionConfig, defaultName: String = TransactionConfig.FAMILY_NAME) {
   self =>
 
diff --git a/akka-core/src/main/scala/stm/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala
index 9d0c1cb309..e5b2ebaba5 100644
--- a/akka-core/src/main/scala/stm/TransactionalMap.scala
+++ b/akka-core/src/main/scala/stm/TransactionalMap.scala
@@ -18,7 +18,7 @@ object TransactionalMap {
 
 /**
  * TODO: documentation
- * 
+ *
  * @author Jonas Bonér
  */
 class TransactionalMap[K, V](initialOpt: Option[HashMap[K, V]] = None) extends Transactional with scala.collection.mutable.Map[K, V] {
diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala
index 9153500cb7..e53bc0f362 100644
--- a/akka-core/src/main/scala/stm/TransactionalVector.scala
+++ b/akka-core/src/main/scala/stm/TransactionalVector.scala
@@ -18,7 +18,7 @@ object TransactionalVector {
 
 /**
  * TODO: documentation
- * 
+ *
  * @author Jonas Bonér
  */
 class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] {

From f8ca6b94c469f3bdb29965a6059e928dfe060e6c Mon Sep 17 00:00:00 2001
From: Peter Vlugter 
Date: Mon, 14 Jun 2010 21:16:35 +1200
Subject: [PATCH 20/38] Added stm local and global package objects

---
 .../src/main/scala/stm/Transaction.scala      | 62 ++-------------
 .../scala/stm/TransactionManagement.scala     | 78 ++++++++++++++++++-
 akka-core/src/main/scala/stm/packages.scala   |  9 +++
 3 files changed, 90 insertions(+), 59 deletions(-)
 create mode 100644 akka-core/src/main/scala/stm/packages.scala

diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala
index 7539c72ae0..06e2b78ac2 100644
--- a/akka-core/src/main/scala/stm/Transaction.scala
+++ b/akka-core/src/main/scala/stm/Transaction.scala
@@ -16,11 +16,8 @@ import se.scalablesolutions.akka.config.Config._
 
 import org.multiverse.api.{Transaction => MultiverseTransaction}
 import org.multiverse.api.lifecycle.{TransactionLifecycleListener, TransactionLifecycleEvent}
-import org.multiverse.api.GlobalStmInstance.getGlobalStmInstance
 import org.multiverse.api.ThreadLocalTransaction._
-import org.multiverse.templates.{TransactionalCallable, OrElseTemplate}
 import org.multiverse.api.{TraceLevel => MultiverseTraceLevel}
-import org.multiverse.api.StmUtils
 
 class NoTransactionInScopeException extends RuntimeException
 class TransactionRetryException(message: String) extends RuntimeException(message)
@@ -46,22 +43,8 @@ object Transaction {
    *
    * @author Jonas Bonér
    */
-  object Local extends TransactionManagement with Logging {
-
-    object DefaultLocalTransactionConfig extends TransactionConfig
-    object DefaultLocalTransactionFactory extends TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction")
-
-    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
-          body
-        }
-       })
-    }
-  }
+  @deprecated("Use the akka.stm.local package object instead.")
+  object Local extends LocalStm
 
   /**
    * Module for "global" transaction management, global in the context of multiple threads.
@@ -79,48 +62,13 @@ object Transaction {
    *
    * @author Jonas Bonér
    */
-  object Global extends TransactionManagement with Logging {
-
-    object DefaultGlobalTransactionConfig extends TransactionConfig
-    object DefaultGlobalTransactionFactory extends TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
-
-    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]() {
-        def call(mtx: MultiverseTransaction): T = {
-          if (!isTransactionSetInScope) createNewTransactionSet
-          factory.addHooks
-          val result = body
-          val txSet = getTransactionSetInScope
-          log.trace("Committing transaction [%s]\n\tby joining transaction set [%s]", mtx, txSet)
-          // FIXME ? txSet.tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS)
-          txSet.joinCommit(mtx)
-          clearTransaction
-          result
-        }
-      })
-    }
-  }
+  @deprecated("Use the akka.stm.global package object instead.")
+  object Global extends GlobalStm
 
   /**
    * TODO: document
    */
-  object Util {
-
-    def deferred[T](body: => T): Unit = StmUtils.scheduleDeferredTask(new Runnable { def run = body })
-
-    def compensating[T](body: => T): Unit = StmUtils.scheduleCompensatingTask(new Runnable { def run = body })
-
-    def retry = StmUtils.retry
-
-    def either[T](firstBody: => T) = new {
-      def orElse(secondBody: => T) = new OrElseTemplate[T] {
-        def either(mtx: MultiverseTransaction) = firstBody
-        def orelse(mtx: MultiverseTransaction) = secondBody
-      }.execute()
-    }
-  }
+  object Util extends StmUtil
 
   /**
    * Attach an Akka-specific Transaction to the current Multiverse transaction.
diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala
index dd9a4e28df..c92ddc6b51 100644
--- a/akka-core/src/main/scala/stm/TransactionManagement.scala
+++ b/akka-core/src/main/scala/stm/TransactionManagement.scala
@@ -8,8 +8,11 @@ import se.scalablesolutions.akka.util.Logging
 
 import java.util.concurrent.atomic.AtomicBoolean
 
+import org.multiverse.api.StmUtils
 import org.multiverse.api.ThreadLocalTransaction._
+import org.multiverse.api.{Transaction => MultiverseTransaction}
 import org.multiverse.commitbarriers.CountDownCommitBarrier
+import org.multiverse.templates.{TransactionalCallable, OrElseTemplate}
 
 class StmException(msg: String) extends RuntimeException(msg)
 
@@ -20,8 +23,10 @@ class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Tran
 object TransactionManagement extends TransactionManagement {
   import se.scalablesolutions.akka.config.Config._
 
-  val TRANSACTION_ENABLED =      new AtomicBoolean(config.getBool("akka.stm.service", true))
-  val FAIR_TRANSACTIONS =        config.getBool("akka.stm.fair", true)
+  // is this needed?
+  val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", true))
+  // move to stm.global.fair?
+  val FAIR_TRANSACTIONS = config.getBool("akka.stm.fair", true)
 
   def isTransactionalityEnabled = TRANSACTION_ENABLED.get
 
@@ -85,3 +90,72 @@ trait TransactionManagement {
     (option ne null) && option.isDefined
   }
 }
+
+class LocalStm extends TransactionManagement with Logging {
+
+  val DefaultLocalTransactionConfig = TransactionConfig()
+  val DefaultLocalTransactionFactory = TransactionFactory(DefaultLocalTransactionConfig, "DefaultLocalTransaction")
+
+  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
+        body
+      }
+    })
+  }
+}
+
+class GlobalStm extends TransactionManagement with Logging {
+
+  val DefaultGlobalTransactionConfig = TransactionConfig()
+  val DefaultGlobalTransactionFactory = TransactionFactory(DefaultGlobalTransactionConfig, "DefaultGlobalTransaction")
+
+  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]() {
+      def call(mtx: MultiverseTransaction): T = {
+        if (!isTransactionSetInScope) createNewTransactionSet
+        factory.addHooks
+        val result = body
+        val txSet = getTransactionSetInScope
+        log.trace("Committing transaction [%s]\n\tby joining transaction set [%s]", mtx, txSet)
+        // FIXME ? txSet.tryJoinCommit(mtx, TransactionManagement.TRANSACTION_TIMEOUT, TimeUnit.MILLISECONDS)
+        txSet.joinCommit(mtx)
+        clearTransaction
+        result
+      }
+    })
+  }
+}
+
+trait StmUtil {
+
+  def deferred[T](body: => T): Unit = StmUtils.scheduleDeferredTask(new Runnable { def run = body })
+
+  def compensating[T](body: => T): Unit = StmUtils.scheduleCompensatingTask(new Runnable { def run = body })
+
+  def retry = StmUtils.retry
+
+  def either[T](firstBody: => T) = new {
+    def orElse(secondBody: => T) = new OrElseTemplate[T] {
+      def either(mtx: MultiverseTransaction) = firstBody
+      def orelse(mtx: MultiverseTransaction) = secondBody
+    }.execute()
+  }
+}
+
+trait StmCommon {
+  type TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig
+  val TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig
+
+  type TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory
+  val TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory
+
+  type Ref[T] = se.scalablesolutions.akka.stm.Ref[T]
+  val Ref = se.scalablesolutions.akka.stm.Ref
+}
+
diff --git a/akka-core/src/main/scala/stm/packages.scala b/akka-core/src/main/scala/stm/packages.scala
new file mode 100644
index 0000000000..4e4810573d
--- /dev/null
+++ b/akka-core/src/main/scala/stm/packages.scala
@@ -0,0 +1,9 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB 
+ */
+
+package se.scalablesolutions.akka.stm
+
+package object local extends LocalStm with StmUtil with StmCommon
+
+package object global extends GlobalStm with StmUtil with StmCommon

From 2e6a1d66e634984b73a4fe9ccb720524360d139f Mon Sep 17 00:00:00 2001
From: Peter Vlugter 
Date: Thu, 17 Jun 2010 15:47:54 +1200
Subject: [PATCH 21/38] Added transactional package object - includes
 Multiverse data structures

---
 akka-core/src/main/scala/stm/packages.scala | 36 +++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/akka-core/src/main/scala/stm/packages.scala b/akka-core/src/main/scala/stm/packages.scala
index 4e4810573d..4cdbaf7999 100644
--- a/akka-core/src/main/scala/stm/packages.scala
+++ b/akka-core/src/main/scala/stm/packages.scala
@@ -4,6 +4,42 @@
 
 package se.scalablesolutions.akka.stm
 
+/**
+ * For importing 'local' stm.
+ */
 package object local extends LocalStm with StmUtil with StmCommon
 
+/**
+ * For importing 'global' stm.
+ */
 package object global extends GlobalStm with StmUtil with StmCommon
+
+/**
+ * For importing the transactional data structures, including the primitive refs
+ * and transactional data structures from Multiverse.
+ */
+package object transactional {
+  type TransactionalMap[K,V] = se.scalablesolutions.akka.stm.TransactionalMap[K,V]
+  val TransactionalMap =  se.scalablesolutions.akka.stm.TransactionalMap
+
+  type TransactionalVector[T] = se.scalablesolutions.akka.stm.TransactionalVector[T]
+  val TransactionalVector = se.scalablesolutions.akka.stm.TransactionalVector
+
+  type BooleanRef = org.multiverse.transactional.refs.BooleanRef
+  type ByteRef    = org.multiverse.transactional.refs.ByteRef
+  type CharRef    = org.multiverse.transactional.refs.CharRef
+  type DoubleRef  = org.multiverse.transactional.refs.DoubleRef
+  type FloatRef   = org.multiverse.transactional.refs.FloatRef
+  type IntRef     = org.multiverse.transactional.refs.IntRef
+  type LongRef    = org.multiverse.transactional.refs.LongRef
+  type ShortRef   = org.multiverse.transactional.refs.ShortRef
+
+  type TransactionalReferenceArray[T] = org.multiverse.transactional.arrays.TransactionalReferenceArray[T]
+
+  // These won't compile - something to do with vararg constructors? Check for Scala bug.
+
+  // type TransactionalArrayList[T] = org.multiverse.transactional.collections.TransactionalArrayList[T]
+  // type TransactionalLinkedList[T] = org.multiverse.transactional.collections.TransactionalLinkedList[T]
+
+  type TransactionalThreadPoolExecutor = org.multiverse.transactional.executors.TransactionalThreadPoolExecutor
+}

From 5acd2fdc97e835770e67e389639fef338b4cadef Mon Sep 17 00:00:00 2001
From: Peter Vlugter 
Date: Fri, 18 Jun 2010 17:13:10 +1200
Subject: [PATCH 22/38] Added some documentation for stm

---
 .../src/main/scala/stm/Transaction.scala      | 40 +-----------
 .../main/scala/stm/TransactionFactory.scala   | 33 +++++++++-
 .../scala/stm/TransactionManagement.scala     | 63 ++++++++++++++-----
 akka-core/src/main/scala/stm/packages.scala   | 15 ++++-
 4 files changed, 95 insertions(+), 56 deletions(-)

diff --git a/akka-core/src/main/scala/stm/Transaction.scala b/akka-core/src/main/scala/stm/Transaction.scala
index 06e2b78ac2..b4fb0cda4c 100644
--- a/akka-core/src/main/scala/stm/Transaction.scala
+++ b/akka-core/src/main/scala/stm/Transaction.scala
@@ -26,48 +26,12 @@ class StmConfigurationException(message: String) extends RuntimeException(messag
 object Transaction {
   val idFactory = new AtomicLong(-1L)
 
-  /**
-   * Module for "local" transaction management, local in the context of threads.
-   * You should only use these if you do not need to have one transaction span
-   * multiple threads (or Actors).
-   * 

- * Example of atomic transaction management using the atomic block. - *

- *

-   * import se.scalablesolutions.akka.stm.Transaction.Local._
-   *
-   * atomic  {
-   *   .. // do something within a transaction
-   * }
-   * 
- * - * @author Jonas Bonér - */ - @deprecated("Use the akka.stm.local package object instead.") + @deprecated("Use the se.scalablesolutions.akka.stm.local package object instead.") object Local extends LocalStm - /** - * Module for "global" transaction management, global in the context of multiple threads. - * You have to use these if you do need to have one transaction span multiple threads (or Actors). - *

- * Example of atomic transaction management using the atomic block. - *

: - *

-   * import se.scalablesolutions.akka.stm.Transaction.Global._
-   *
-   * atomic  {
-   *   .. // do something within a transaction
-   * }
-   * 
- * - * @author Jonas Bonér - */ - @deprecated("Use the akka.stm.global package object instead.") + @deprecated("Use the se.scalablesolutions.akka.stm.global package object instead.") object Global extends GlobalStm - /** - * TODO: document - */ object Util extends StmUtil /** diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala index 2269e0edf7..7c6cfce536 100644 --- a/akka-core/src/main/scala/stm/TransactionFactory.scala +++ b/akka-core/src/main/scala/stm/TransactionFactory.scala @@ -13,7 +13,7 @@ import org.multiverse.templates.TransactionBoilerplate import org.multiverse.api.TraceLevel /** - * For configuring multiverse transactions. + * For configuring multiverse transactions. See TransactionConfig class for options. */ object TransactionConfig { val FAMILY_NAME = "DefaultTransaction" @@ -57,6 +57,19 @@ object TransactionConfig { /** * For configuring multiverse transactions. + * + * @param familyName Family name for transactions. Useful for debugging. + * @param readonly Sets transaction as readonly. Readonly transactions are cheaper. + * @param maxRetries The maximum number of times a transaction will retry. + * @param timeout The maximum time a transaction will block for. + * @param trackReads Whether all reads should be tracked. Needed for blocking operations. + * @param writeSkew Whether writeskew is allowed. Disable with care. + * @param explicitRetries Whether explicit retries are allowed. + * @param interruptible Whether a blocking transaction can be interrupted. + * @param speculative Whether speculative configuration should be enabled. + * @param quickRelease Whether locks should be released as quickly as possible (before whole commit). + * @param traceLevel Transaction trace level. + * @param hooks Whether hooks for persistence modules and JTA should be added to the transaction. */ class TransactionConfig(val familyName: String = TransactionConfig.FAMILY_NAME, val readonly: Boolean = TransactionConfig.READONLY, @@ -75,6 +88,24 @@ object DefaultTransactionConfig extends TransactionConfig /** * Wrapper for transaction config, factory, and boilerplate. Used by atomic. + * Can be passed to atomic implicitly or explicitly. + *

+ *

+ * implicit val txFactory = TransactionFactory(readonly = true)
+ * ...
+ * atomic {
+ *   // do something within a readonly transaction
+ * }
+ * 
+ *

+ * Can be created at different levels as needed. For example: as an implicit object + * used throughout a package, as a static implicit val within a singleton object and + * imported where needed, or as an implicit val within each instance of a class. + *

+ * See TransactionConfig for configuration options. + *

+ * If no explicit transactin factory is passed to atomic and there is no implicit + * transaction factory in scope, then a default transaction factory is used. */ object TransactionFactory { def apply(config: TransactionConfig) = new TransactionFactory(config) diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index c92ddc6b51..2b4b9f888c 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -8,7 +8,7 @@ import se.scalablesolutions.akka.util.Logging import java.util.concurrent.atomic.AtomicBoolean -import org.multiverse.api.StmUtils +import org.multiverse.api.{StmUtils => MultiverseStmUtils} import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.commitbarriers.CountDownCommitBarrier @@ -91,6 +91,21 @@ trait TransactionManagement { } } +/** + * Local transaction management, local in the context of threads. + * Use this if you do not need to have one transaction span + * multiple threads (or Actors). + *

+ * Example of atomic transaction management using the atomic block. + *

+ *

+ * import se.scalablesolutions.akka.stm.local._
+ *
+ * atomic  {
+ *   // do something within a transaction
+ * }
+ * 
+ */ class LocalStm extends TransactionManagement with Logging { val DefaultLocalTransactionConfig = TransactionConfig() @@ -108,6 +123,20 @@ class LocalStm extends TransactionManagement with Logging { } } +/** + * Global transaction management, global in the context of multiple threads. + * You this if you need to have one transaction span multiple threads (or Actors). + *

+ * Example of atomic transaction management using the atomic block: + *

+ *

+ * import se.scalablesolutions.akka.stm.global._
+ *
+ * atomic  {
+ *   // do something within a transaction
+ * }
+ * 
+ */ class GlobalStm extends TransactionManagement with Logging { val DefaultGlobalTransactionConfig = TransactionConfig() @@ -133,13 +162,27 @@ class GlobalStm extends TransactionManagement with Logging { } trait StmUtil { + /** + * Schedule a deferred task on the thread local transaction (use within an atomic). + * This is executed when the transaction commits. + */ + def deferred[T](body: => T): Unit = MultiverseStmUtils.scheduleDeferredTask(new Runnable { def run = body }) - def deferred[T](body: => T): Unit = StmUtils.scheduleDeferredTask(new Runnable { def run = body }) + /** + * Schedule a compensating task on the thread local transaction (use within an atomic). + * This is executed when the transaction aborts. + */ + def compensating[T](body: => T): Unit = MultiverseStmUtils.scheduleCompensatingTask(new Runnable { def run = body }) - def compensating[T](body: => T): Unit = StmUtils.scheduleCompensatingTask(new Runnable { def run = body }) - - def retry = StmUtils.retry + /** + * STM retry. Use within an atomic. + * Blocks the current transaction. Can be used to wait for a condition. + */ + def retry = MultiverseStmUtils.retry + /** + * Use either-orElse to combine two blocking transactions. + */ def either[T](firstBody: => T) = new { def orElse(secondBody: => T) = new OrElseTemplate[T] { def either(mtx: MultiverseTransaction) = firstBody @@ -148,14 +191,4 @@ trait StmUtil { } } -trait StmCommon { - type TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig - val TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig - - type TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory - val TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory - - type Ref[T] = se.scalablesolutions.akka.stm.Ref[T] - val Ref = se.scalablesolutions.akka.stm.Ref -} diff --git a/akka-core/src/main/scala/stm/packages.scala b/akka-core/src/main/scala/stm/packages.scala index 4cdbaf7999..cbb3ad4804 100644 --- a/akka-core/src/main/scala/stm/packages.scala +++ b/akka-core/src/main/scala/stm/packages.scala @@ -5,15 +5,26 @@ package se.scalablesolutions.akka.stm /** - * For importing 'local' stm. + * For importing 'local' STM. */ package object local extends LocalStm with StmUtil with StmCommon /** - * For importing 'global' stm. + * For importing 'global' STM. */ package object global extends GlobalStm with StmUtil with StmCommon +trait StmCommon { + type TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig + val TransactionConfig = se.scalablesolutions.akka.stm.TransactionConfig + + type TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory + val TransactionFactory = se.scalablesolutions.akka.stm.TransactionFactory + + type Ref[T] = se.scalablesolutions.akka.stm.Ref[T] + val Ref = se.scalablesolutions.akka.stm.Ref +} + /** * For importing the transactional data structures, including the primitive refs * and transactional data structures from Multiverse. From 58be9f03bcc8df7e15fd9e77d0978286772e8e83 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 18 Jun 2010 20:49:40 +1200 Subject: [PATCH 23/38] Updated imports to use stm package objects --- akka-core/src/main/scala/actor/ActorRef.scala | 4 ++-- akka-core/src/test/scala/RefSpec.scala | 2 +- akka-core/src/test/scala/StmSpec.scala | 18 +++++++++--------- .../scala/RedisInconsistentSizeBugTest.scala | 2 +- .../akka-sample-ants/src/main/scala/Ants.scala | 3 +-- .../akka-sample-ants/src/main/spde/Ants.spde | 2 +- .../src/main/scala/ChatServer.scala | 2 +- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index 5413f4fc2a..1af1d44341 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -8,8 +8,8 @@ import se.scalablesolutions.akka.dispatch._ import se.scalablesolutions.akka.config.Config.config import se.scalablesolutions.akka.config.{AllForOneStrategy, OneForOneStrategy, FaultHandlingStrategy} import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.stm.Transaction.Global._ -import se.scalablesolutions.akka.stm.{TransactionConfig, TransactionFactory, TransactionManagement} +import se.scalablesolutions.akka.stm.global._ +import se.scalablesolutions.akka.stm.TransactionManagement import se.scalablesolutions.akka.stm.TransactionManagement._ import se.scalablesolutions.akka.stm.TransactionManagement import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ diff --git a/akka-core/src/test/scala/RefSpec.scala b/akka-core/src/test/scala/RefSpec.scala index 3b1a1fe3ae..805e0834ea 100644 --- a/akka-core/src/test/scala/RefSpec.scala +++ b/akka-core/src/test/scala/RefSpec.scala @@ -11,7 +11,7 @@ import se.scalablesolutions.akka.actor.Actor._ class RefSpec extends Spec with ShouldMatchers { describe("A Ref") { - import Transaction.Local._ + import local._ it("should optionally accept an initial value") { val emptyRef = Ref[Int] diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala index f08116f4af..e82d414800 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/StmSpec.scala @@ -16,9 +16,9 @@ class StmSpec extends ShouldMatchers with BeforeAndAfterAll { - describe("Transaction.Local") { + describe("Local STM") { it("should be able to do multiple consecutive atomic {..} statements") { - import Transaction.Local._ + import local._ lazy val ref = Ref[Int]() @@ -37,7 +37,7 @@ class StmSpec extends } it("should be able to do nested atomic {..} statements") { - import Transaction.Local._ + import local._ lazy val ref = Ref[Int]() @@ -59,7 +59,7 @@ class StmSpec extends } it("should roll back failing nested atomic {..} statements") { - import Transaction.Local._ + import local._ lazy val ref = Ref[Int]() @@ -82,7 +82,7 @@ class StmSpec extends } } - describe("Transaction.Global") { + describe("Global STM") { it("should be able to initialize with atomic {..} block inside actor constructor") { import GlobalTransactionVectorTestActor._ try { @@ -181,17 +181,17 @@ object GlobalTransactionVectorTestActor { } class GlobalTransactionVectorTestActor extends Actor { import GlobalTransactionVectorTestActor._ - import se.scalablesolutions.akka.stm.Transaction.Global + import se.scalablesolutions.akka.stm.global._ - private val vector: TransactionalVector[Int] = Global.atomic { TransactionalVector(1) } + private val vector: TransactionalVector[Int] = atomic { TransactionalVector(1) } def receive = { case Add(value) => - Global.atomic { vector + value} + atomic { vector + value} self.reply(Success) case Size => - val size = Global.atomic { vector.size } + val size = atomic { vector.size } self.reply(size) } } diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala index 22d294c735..dde431628a 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala @@ -8,7 +8,7 @@ import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.config.OneForOneStrategy import Actor._ import se.scalablesolutions.akka.persistence.common.PersistentVector -import se.scalablesolutions.akka.stm.Transaction.Global._ +import se.scalablesolutions.akka.stm.global._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.util.Logging diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala index ccfdf21581..371a7792b0 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -9,8 +9,7 @@ import scala.util.Random.{nextInt => randomInt} import se.scalablesolutions.akka import akka.actor.{ActorRef, Transactor, Scheduler} import akka.actor.Actor.actorOf -import akka.stm._ -import akka.stm.Transaction.Local._ +import akka.stm.local._ object Config { val Dim = 80 // dimensions of square world diff --git a/akka-samples/akka-sample-ants/src/main/spde/Ants.spde b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde index 1d5bf4c39f..2cbb7ae44d 100644 --- a/akka-samples/akka-sample-ants/src/main/spde/Ants.spde +++ b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde @@ -1,6 +1,6 @@ import sample.ants._ import sample.ants.Config._ -import se.scalablesolutions.akka.stm.Transaction.Local._ +import se.scalablesolutions.akka.stm.local._ val scale = 5 diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index f244f8eeef..abe76ebcfa 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -10,7 +10,7 @@ import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor, ActorRef, Remo import se.scalablesolutions.akka.remote.{RemoteNode, RemoteClient} import se.scalablesolutions.akka.persistence.common.PersistentVector import se.scalablesolutions.akka.persistence.redis.RedisStorage -import se.scalablesolutions.akka.stm.Transaction.Global._ +import se.scalablesolutions.akka.stm.global._ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.config.OneForOneStrategy import se.scalablesolutions.akka.util.Logging From 024d225bab293b3342e40d44265d0d46d8b2e6f7 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Fri, 18 Jun 2010 21:19:52 +1200 Subject: [PATCH 24/38] Using actor id for transaction family name --- akka-core/src/main/scala/actor/ActorRef.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index 1af1d44341..c27bfe161a 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -9,7 +9,6 @@ import se.scalablesolutions.akka.config.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._ import se.scalablesolutions.akka.stm.TransactionManagement import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ @@ -927,7 +926,7 @@ sealed class LocalActorRef private[akka]( dispatcher.register(this) dispatcher.start if (isTransactor) { - _transactionFactory = Some(TransactionFactory(_transactionConfig, actorClass.getName)) + _transactionFactory = Some(TransactionFactory(_transactionConfig, id)) } _isRunning = true if (!isInInitialization) initializeActorInstance From 12b83ebeb1e16b703a4f79a72ac4568eef5f09f9 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sat, 19 Jun 2010 17:41:35 +1200 Subject: [PATCH 25/38] Moved data flow to its own package --- .../src/main/scala/{stm => dataflow}/DataFlowVariable.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename akka-core/src/main/scala/{stm => dataflow}/DataFlowVariable.scala (99%) diff --git a/akka-core/src/main/scala/stm/DataFlowVariable.scala b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala similarity index 99% rename from akka-core/src/main/scala/stm/DataFlowVariable.scala rename to akka-core/src/main/scala/dataflow/DataFlowVariable.scala index 752c71cead..8fa67e8269 100644 --- a/akka-core/src/main/scala/stm/DataFlowVariable.scala +++ b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala @@ -2,7 +2,7 @@ * Copyright (C) 2009-2010 Scalable Solutions AB */ -package se.scalablesolutions.akka.stm +package se.scalablesolutions.akka.dataflow import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.{ConcurrentLinkedQueue, LinkedBlockingQueue} From 3f7402ceff99c3cef3fde3f0dc603a5362b33034 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sat, 19 Jun 2010 17:56:25 +1200 Subject: [PATCH 26/38] Removing unused stm classes --- .../src/main/scala/stm/ResultOrFailure.scala | 58 ---------- .../main/scala/stm/TransactionWatcher.scala | 105 ------------------ 2 files changed, 163 deletions(-) delete mode 100644 akka-core/src/main/scala/stm/ResultOrFailure.scala delete mode 100644 akka-core/src/main/scala/stm/TransactionWatcher.scala diff --git a/akka-core/src/main/scala/stm/ResultOrFailure.scala b/akka-core/src/main/scala/stm/ResultOrFailure.scala deleted file mode 100644 index 6ddf8c9751..0000000000 --- a/akka-core/src/main/scala/stm/ResultOrFailure.scala +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.stm - -/** - * Reference that can hold either a typed value or an exception. - * - * Usage: - *
- * scala> ResultOrFailure(1)
- * res0: ResultOrFailure[Int] = ResultOrFailure@a96606
- *
- * scala> res0()
- * res1: Int = 1
- *
- * scala> res0() = 3
- *
- * scala> res0()
- * res3: Int = 3
- *
- * scala> res0() = { println("Hello world"); 3}
- * Hello world
- *
- * scala> res0()
- * res5: Int = 3
- *
- * scala> res0() = error("Lets see what happens here...")
- *
- * scala> res0()
- * java.lang.RuntimeException: Lets see what happens here...
- *      at ResultOrFailure.apply(RefExcept.scala:11)
- *      at .(:6)
- *      at .()
- *      at Re...
- * 
- * - * @author Jonas Bonér - */ -class ResultOrFailure[Payload](payload: Payload, val tx: Option[Transaction]) { - private[this] var contents: Either[Throwable, Payload] = Right(payload) - - def update(value: => Payload) = { - contents = try { Right(value) } catch { case (e : Throwable) => Left(e) } - } - - def apply() = contents match { - case Right(payload) => payload - case Left(e) => throw e - } - - override def toString(): String = "ResultOrFailure[" + contents + "]" -} -object ResultOrFailure { - def apply[Payload](payload: Payload, tx: Option[Transaction]) = new ResultOrFailure(payload, tx) - def apply[AnyRef](tx: Option[Transaction]) = new ResultOrFailure(new Object, tx) -} diff --git a/akka-core/src/main/scala/stm/TransactionWatcher.scala b/akka-core/src/main/scala/stm/TransactionWatcher.scala deleted file mode 100644 index 400bc9b763..0000000000 --- a/akka-core/src/main/scala/stm/TransactionWatcher.scala +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package se.scalablesolutions.akka.stm - -/* -import kernel.util.Logging -import org.apache.zookeeper.jmx.ManagedUtil -import org.apache.zookeeper.server.persistence.FileTxnSnapLog -import org.apache.zookeeper.server.{ServerConfig, NIOServerCnxn} -import org.apache.zookeeper.{KeeperException, WatchedEvent, Watcher, ZooKeeper, DataMonitor} -*/ -/** - * @author Jonas Bonér - * -class TransactionWatcher extends Logging with Watcher { - - val SERVER_URL = "localhost" - - val ZOO_KEEPER_URL = SERVER_URL - val ZOO_KEEPER_PORT = 2181 - val znode = "master" - - private[this] val db = new scala.collection.mutable.HashMap[String, String] - - private[this] val zk = new ZooKeeper(ZOO_KEEPER_URL + ":" + ZOO_KEEPER_PORT, 3000, this) - private[this] val dm = new DataMonitor(zk, znode, null, this) - - override def process(event: WatchedEvent) = { - log.debug("New ZooKeeper event: %s", event) - val path = event.getPath(); - if (event.getType == Event.EventType.None) { - // We are are being told that the state of the connection has changed - event.getState match { - case SyncConnected => - // In this particular example we don't need to do anything - // here - watches are automatically re-registered with - // server and any watches triggered while the client was - // disconnected will be delivered (in order of course) - case Expired => - dead = true - listener.closing(KeeperException.Code.SessionExpired) - } - } else { - if (path != null && path.equals(znode)) { - // Something has changed on the node, let's find out - zk.exists(znode, true, this, null) - } - } - if (chainedWatcher ne null) chainedWatcher.process(event); - } - - - - def run: Unit = synchronized { - try { - while (!dm.dead) wait - } catch { - case e: InterruptedException => Thread.currentThread.interrupt - } - } - - def closing(rc: Int): Unit = synchronized { notifyAll() } -} - - */ -object TransactionWatcher { - def main(args: Array[String]): Unit = { - println("Connecting to ZooKeeper...") - //new TransactionWatcher - } -} - - // private[akka] def startZooKeeper = { - // try { - // ManagedUtil.registerLog4jMBeans - // ServerConfig.parse(args) - // } catch { - // case e: JMException => log.warning("Unable to register log4j JMX control: s%", e) - // case e => log.fatal("Error in ZooKeeper config: s%", e) - // } - // val factory = new ZooKeeperServer.Factory() { - // override def createConnectionFactory = new NIOServerCnxn.Factory(ServerConfig.getClientPort) - // override def createServer = { - // val server = new ZooKeeperServer - // val txLog = new FileTxnSnapLog( - // new File(ServerConfig.getDataLogDir), - // new File(ServerConfig.getDataDir)) - // server.setTxnLogFactory(txLog) - // server - // } - // } - // try { - // val zooKeeper = factory.createServer - // zooKeeper.startup - // log.info("ZooKeeper started") - // // TODO: handle clean shutdown as below in separate thread - // // val cnxnFactory = serverFactory.createConnectionFactory - // // cnxnFactory.setZooKeeperServer(zooKeeper) - // // cnxnFactory.join - // // if (zooKeeper.isRunning) zooKeeper.shutdown - // } catch { case e => log.fatal("Unexpected exception: s%",e) } - // } - From 18ed80b160df9524e6e80fab1d86e74e7d1073ec Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sun, 20 Jun 2010 09:25:26 +1200 Subject: [PATCH 27/38] Updated ants sample --- .../src/main/scala/Ants.scala | 39 +++++++++++++------ .../akka-sample-ants/src/main/spde/Ants.spde | 8 ++-- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala index 371a7792b0..f1cc0ba628 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -7,13 +7,13 @@ package sample.ants import java.util.concurrent.TimeUnit import scala.util.Random.{nextInt => randomInt} import se.scalablesolutions.akka -import akka.actor.{ActorRef, Transactor, Scheduler} +import akka.actor.{Actor, ActorRef, Scheduler} import akka.actor.Actor.actorOf import akka.stm.local._ object Config { val Dim = 80 // dimensions of square world - val AntsSqrt = 7 // number of ants = AntsSqrt^2 + val AntsSqrt = 20 // number of ants = AntsSqrt^2 val FoodPlaces = 35 // number of places with food val FoodRange = 100 // range of amount of food at a place val PherScale = 10 // scale factor for pheromone drawing @@ -41,7 +41,7 @@ case class Cell(food: Int = 0, pher: Float = 0, ant: Option[Ant] = None, home: B object EmptyCell extends Cell class Place(initCell: Cell = EmptyCell) extends Ref(Some(initCell)) { - def cell: Cell = get.get + def cell: Cell = getOrElse(EmptyCell) def food: Int = cell.food def food(i: Int) = alter(_.addFood(i)) def hasFood = food > 0 @@ -58,6 +58,8 @@ class Place(initCell: Cell = EmptyCell) extends Ref(Some(initCell)) { def home: Boolean = cell.home } +case object Ping + object World { import Config._ @@ -66,6 +68,10 @@ object World { lazy val ants = setup lazy val evaporator = actorOf[Evaporator].start + private val snapshotFactory = TransactionFactory(readonly = true, familyName = "snapshot") + + def snapshot = atomic(snapshotFactory) { Array.tabulate(Dim, Dim)(place(_, _).get) } + def place(loc: (Int, Int)) = places(loc._1)(loc._2) private def setup = atomic { @@ -81,12 +87,12 @@ object World { } def start = { - ants foreach (pingEvery(_, AntMillis)) - pingEvery(evaporator, EvapMillis) + ants foreach pingEvery(AntMillis) + pingEvery(EvapMillis)(evaporator) } - private def pingEvery(actor: ActorRef, millis: Long) = - Scheduler.schedule(actor, "ping", Config.StartDelay, millis, TimeUnit.MILLISECONDS) + private def pingEvery(millis: Long)(actor: ActorRef) = + Scheduler.schedule(actor, Ping, Config.StartDelay, millis, TimeUnit.MILLISECONDS) } object Util { @@ -121,9 +127,9 @@ object Util { } } -trait WorldActor extends Transactor { +trait WorldActor extends Actor { def act - def receive = { case "ping" => act } + def receive = { case Ping => act } } class AntActor(initLoc: (Int, Int)) extends WorldActor { @@ -131,13 +137,17 @@ class AntActor(initLoc: (Int, Int)) extends WorldActor { import Util._ val locRef = Ref(initLoc) + + val name = "ant-from-" + initLoc._1 + "-" + initLoc._2 + implicit val txFactory = TransactionFactory(familyName = name) + val homing = (p: Place) => p.pher + (100 * (if (p.home) 0 else 1)) val foraging = (p: Place) => p.pher + p.food - def loc = locRef.get.getOrElse(initLoc) + def loc = locRef.getOrElse(initLoc) def newLoc(l: (Int, Int)) = locRef swap l - def act = { + def act = atomic { val (x, y) = loc val current = place(x, y) for (ant <- current.ant) { @@ -200,6 +210,11 @@ class AntActor(initLoc: (Int, Int)) extends WorldActor { class Evaporator extends WorldActor { import Config._ import World._ + + implicit val txFactory = TransactionFactory(familyName = "evaporator") val evaporate = (pher: Float) => pher * EvapRate - def act = for (x <- 0 until Dim; y <- 0 until Dim) place(x, y) pher evaporate + + def act = for (x <- 0 until Dim; y <- 0 until Dim) { + atomic { place(x, y) pher evaporate } + } } diff --git a/akka-samples/akka-sample-ants/src/main/spde/Ants.spde b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde index 2cbb7ae44d..05565673d6 100644 --- a/akka-samples/akka-sample-ants/src/main/spde/Ants.spde +++ b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde @@ -8,13 +8,13 @@ size(Dim * scale, Dim * scale) smooth() override def setup() { - background(255) + background(255) World.start } def draw() { - for (x <- 0 until Dim; y <- 0 until Dim) { - val cell = atomic { World.place(x, y).cell } + val world = World.snapshot + for (x <- 0 until Dim; y <- 0 until Dim; cell <- world(x)(y)) { val (rx, ry, rw, rh) = (x * scale, y * scale, scale, scale) noStroke() fill(255) @@ -39,7 +39,7 @@ val s = scale - 1 val m = s / 2 def antLine(dir: Int) = dir match { - case 0|4 => (m, 0, m, s) + case 0|4 => (m, 0, m, s) case 1|5 => (s, 0, 0, s) case 2|6 => (s, m, 0, m) case _ => (s, s, 0, 0) From 71fedd912860c8e74d7db36f7032d4e164eb6131 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sun, 20 Jun 2010 10:10:09 +1200 Subject: [PATCH 28/38] Removed isTransactionalityEnabled --- akka-core/src/main/scala/actor/ActorRef.scala | 22 ++----------------- .../scala/stm/TransactionManagement.scala | 6 ----- config/akka-reference.conf | 3 +-- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index c27bfe161a..a4a1d63490 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -1198,7 +1198,7 @@ sealed class LocalActorRef private[akka]( /** * Callback for the dispatcher. This is the ingle entry point to the user Actor implementation. */ - protected[akka] def invoke(messageHandle: MessageInvocation): Unit = actor.synchronized { + protected[akka] def invoke(messageHandle: MessageInvocation): Unit = actor.synchronized { if (isShutdown) { Actor.log.warning("Actor [%s] is shut down, ignoring message [%s]", toString, messageHandle) return @@ -1206,8 +1206,7 @@ sealed class LocalActorRef private[akka]( sender = messageHandle.sender senderFuture = messageHandle.senderFuture try { - if (TransactionManagement.isTransactionalityEnabled) transactionalDispatch(messageHandle) - else dispatch(messageHandle) + dispatch(messageHandle) } catch { case e => Actor.log.error(e, "Could not invoke actor [%s]", this) @@ -1216,23 +1215,6 @@ sealed class LocalActorRef private[akka]( } private def dispatch[T](messageHandle: MessageInvocation) = { - val message = messageHandle.message //serializeMessage(messageHandle.message) - setTransactionSet(messageHandle.transactionSet) - try { - actor.base(message) - } catch { - case e => - _isBeingRestarted = true - Actor.log.error(e, "Could not invoke actor [%s]", toString) - // FIXME to fix supervisor restart of remote actor for oneway calls, inject a supervisor proxy that can send notification back to client - if (_supervisor.isDefined) _supervisor.get ! Exit(this, e) - senderFuture.foreach(_.completeWithException(this, e)) - } finally { - clearTransaction - } - } - - private def transactionalDispatch[T](messageHandle: MessageInvocation) = { val message = messageHandle.message //serializeMessage(messageHandle.message) var topLevelTransaction = false val txSet: Option[CountDownCommitBarrier] = diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index 2b4b9f888c..44cb76a6d3 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -23,15 +23,9 @@ class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Tran object TransactionManagement extends TransactionManagement { import se.scalablesolutions.akka.config.Config._ - // is this needed? - val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", true)) // move to stm.global.fair? val FAIR_TRANSACTIONS = config.getBool("akka.stm.fair", true) - def isTransactionalityEnabled = TRANSACTION_ENABLED.get - - def disableTransactions = TRANSACTION_ENABLED.set(false) - private[akka] val transactionSet = new ThreadLocal[Option[CountDownCommitBarrier]]() { override protected def initialValue: Option[CountDownCommitBarrier] = None } diff --git a/config/akka-reference.conf b/config/akka-reference.conf index f7f9f0abc5..e20a745ca1 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -31,8 +31,7 @@ - service = on - fair = on # should transactions be fair or non-fair (non fair yield better performance) + fair = on # should global transactions be fair or non-fair (non fair yield better performance) jta-aware = off # 'on' means that if there JTA Transaction Manager available then the STM will # begin (or join), commit or rollback the JTA transaction. Default is 'off'. From f8da5e14a137fdf0c883027ba8c57c16203d5db0 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sun, 20 Jun 2010 12:49:49 +1200 Subject: [PATCH 29/38] Improved transaction factory defaults --- .../main/scala/stm/TransactionFactory.scala | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala index 7c6cfce536..26c255e6cf 100644 --- a/akka-core/src/main/scala/stm/TransactionFactory.scala +++ b/akka-core/src/main/scala/stm/TransactionFactory.scala @@ -4,6 +4,8 @@ package se.scalablesolutions.akka.stm +import java.lang.{Boolean => JBoolean} + import se.scalablesolutions.akka.config.Config._ import se.scalablesolutions.akka.util.Duration @@ -16,16 +18,17 @@ import org.multiverse.api.TraceLevel * For configuring multiverse transactions. See TransactionConfig class for options. */ object TransactionConfig { + // note: null values are so that we can default to Multiverse inference when not set val FAMILY_NAME = "DefaultTransaction" - val READONLY = false + val READONLY = null.asInstanceOf[JBoolean] val MAX_RETRIES = config.getInt("akka.stm.max-retries", 1000) val TIMEOUT = config.getLong("akka.stm.timeout", Long.MaxValue) val TIME_UNIT = config.getString("akka.stm.time-unit", "seconds") - val TRACK_READS = config.getBool("akka.stm.track-reads", false) + val TRACK_READS = null.asInstanceOf[JBoolean] val WRITE_SKEW = config.getBool("akka.stm.write-skew", true) val EXPLICIT_RETRIES = config.getBool("akka.stm.explicit-retries", false) val INTERRUPTIBLE = config.getBool("akka.stm.interruptible", false) - val SPECULATIVE = config.getBool("akka.stm.speculative", false) + val SPECULATIVE = config.getBool("akka.stm.speculative", true) val QUICK_RELEASE = config.getBool("akka.stm.quick-release", true) val TRACE_LEVEL = traceLevel(config.getString("akka.stm.trace-level", "none")) val HOOKS = config.getBool("akka.stm.hooks", true) @@ -39,10 +42,10 @@ object TransactionConfig { } def apply(familyName: String = FAMILY_NAME, - readonly: Boolean = READONLY, + readonly: JBoolean = READONLY, maxRetries: Int = MAX_RETRIES, timeout: Duration = DefaultTimeout, - trackReads: Boolean = TRACK_READS, + trackReads: JBoolean = TRACK_READS, writeSkew: Boolean = WRITE_SKEW, explicitRetries: Boolean = EXPLICIT_RETRIES, interruptible: Boolean = INTERRUPTIBLE, @@ -72,10 +75,10 @@ object TransactionConfig { * @param hooks Whether hooks for persistence modules and JTA should be added to the transaction. */ class TransactionConfig(val familyName: String = TransactionConfig.FAMILY_NAME, - val readonly: Boolean = TransactionConfig.READONLY, + val readonly: JBoolean = TransactionConfig.READONLY, val maxRetries: Int = TransactionConfig.MAX_RETRIES, val timeout: Duration = TransactionConfig.DefaultTimeout, - val trackReads: Boolean = TransactionConfig.TRACK_READS, + val trackReads: JBoolean = TransactionConfig.TRACK_READS, val writeSkew: Boolean = TransactionConfig.WRITE_SKEW, val explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES, val interruptible: Boolean = TransactionConfig.INTERRUPTIBLE, @@ -113,10 +116,10 @@ object TransactionFactory { def apply(config: TransactionConfig, defaultName: String) = new TransactionFactory(config, defaultName) def apply(familyName: String = TransactionConfig.FAMILY_NAME, - readonly: Boolean = TransactionConfig.READONLY, + readonly: JBoolean = TransactionConfig.READONLY, maxRetries: Int = TransactionConfig.MAX_RETRIES, timeout: Duration = TransactionConfig.DefaultTimeout, - trackReads: Boolean = TransactionConfig.TRACK_READS, + trackReads: JBoolean = TransactionConfig.TRACK_READS, writeSkew: Boolean = TransactionConfig.WRITE_SKEW, explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES, interruptible: Boolean = TransactionConfig.INTERRUPTIBLE, @@ -139,19 +142,28 @@ class TransactionFactory(val config: TransactionConfig = DefaultTransactionConfi // use the config family name if it's been set, otherwise defaultName - used by actors to set class name as default val familyName = if (config.familyName != TransactionConfig.FAMILY_NAME) config.familyName else defaultName - val factory = (getGlobalStmInstance().asInstanceOf[AlphaStm].getTransactionFactoryBuilder() - .setFamilyName(familyName) - .setReadonly(config.readonly) - .setMaxRetries(config.maxRetries) - .setTimeoutNs(config.timeout.toNanos) - .setReadTrackingEnabled(config.trackReads) - .setWriteSkewAllowed(config.writeSkew) - .setExplicitRetryAllowed(config.explicitRetries) - .setInterruptible(config.interruptible) - .setSpeculativeConfigurationEnabled(config.speculative) - .setQuickReleaseEnabled(config.quickRelease) - .setTraceLevel(config.traceLevel) - .build()) + val factory = { + var builder = (getGlobalStmInstance().asInstanceOf[AlphaStm].getTransactionFactoryBuilder() + .setFamilyName(familyName) + .setMaxRetries(config.maxRetries) + .setTimeoutNs(config.timeout.toNanos) + .setWriteSkewAllowed(config.writeSkew) + .setExplicitRetryAllowed(config.explicitRetries) + .setInterruptible(config.interruptible) + .setSpeculativeConfigurationEnabled(config.speculative) + .setQuickReleaseEnabled(config.quickRelease) + .setTraceLevel(config.traceLevel)) + + if (config.readonly ne null) { + builder = builder.setReadonly(config.readonly.booleanValue) + } // otherwise default to Multiverse inference + + if (config.trackReads ne null) { + builder = builder.setReadTrackingEnabled(config.trackReads.booleanValue) + } // otherwise default to Multiverse inference + + builder.build() + } val boilerplate = new TransactionBoilerplate(factory) From 104c3e744d30f42f92b97d277054f6246ab14d35 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Sun, 20 Jun 2010 16:40:18 +1200 Subject: [PATCH 30/38] Some stm documentation changes --- akka-core/src/main/scala/actor/ActorRef.scala | 8 -- .../main/scala/stm/TransactionFactory.scala | 80 +++++++++++-------- .../scala/stm/TransactionManagement.scala | 6 +- .../src/main/scala/stm/TransactionalMap.scala | 2 +- .../main/scala/stm/TransactionalVector.scala | 2 +- akka-core/src/main/scala/util/Duration.scala | 46 +++++------ 6 files changed, 76 insertions(+), 68 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActorRef.scala b/akka-core/src/main/scala/actor/ActorRef.scala index a4a1d63490..42df4e1305 100644 --- a/akka-core/src/main/scala/actor/ActorRef.scala +++ b/akka-core/src/main/scala/actor/ActorRef.scala @@ -503,10 +503,6 @@ trait ActorRef extends TransactionManagement { /** * Invoking 'makeTransactionRequired' means that the actor will **start** a new transaction if non exists. * However, it will always participate in an existing transaction. - * If transactionality want to be completely turned off then do it by invoking: - *
-   *  TransactionManagement.disableTransactions
-   * 
*/ def makeTransactionRequired: Unit @@ -879,10 +875,6 @@ sealed class LocalActorRef private[akka]( /** * Invoking 'makeTransactionRequired' means that the actor will **start** a new transaction if non exists. * However, it will always participate in an existing transaction. - * If transactionality want to be completely turned off then do it by invoking: - *
-   *  TransactionManagement.disableTransactions
-   * 
*/ def makeTransactionRequired = guard.withGuard { if (!isRunning || isBeingRestarted) isTransactor = true diff --git a/akka-core/src/main/scala/stm/TransactionFactory.scala b/akka-core/src/main/scala/stm/TransactionFactory.scala index 26c255e6cf..a7a81c4212 100644 --- a/akka-core/src/main/scala/stm/TransactionFactory.scala +++ b/akka-core/src/main/scala/stm/TransactionFactory.scala @@ -15,7 +15,7 @@ import org.multiverse.templates.TransactionBoilerplate import org.multiverse.api.TraceLevel /** - * For configuring multiverse transactions. See TransactionConfig class for options. + * For configuring multiverse transactions. */ object TransactionConfig { // note: null values are so that we can default to Multiverse inference when not set @@ -41,6 +41,22 @@ object TransactionConfig { case _ => Transaction.TraceLevel.None } + /** + * For configuring multiverse transactions. + * + * @param familyName Family name for transactions. Useful for debugging. + * @param readonly Sets transaction as readonly. Readonly transactions are cheaper. + * @param maxRetries The maximum number of times a transaction will retry. + * @param timeout The maximum time a transaction will block for. + * @param trackReads Whether all reads should be tracked. Needed for blocking operations. + * @param writeSkew Whether writeskew is allowed. Disable with care. + * @param explicitRetries Whether explicit retries are allowed. + * @param interruptible Whether a blocking transaction can be interrupted. + * @param speculative Whether speculative configuration should be enabled. + * @param quickRelease Whether locks should be released as quickly as possible (before whole commit). + * @param traceLevel Transaction trace level. + * @param hooks Whether hooks for persistence modules and JTA should be added to the transaction. + */ def apply(familyName: String = FAMILY_NAME, readonly: JBoolean = READONLY, maxRetries: Int = MAX_RETRIES, @@ -60,19 +76,19 @@ object TransactionConfig { /** * For configuring multiverse transactions. - * - * @param familyName Family name for transactions. Useful for debugging. - * @param readonly Sets transaction as readonly. Readonly transactions are cheaper. - * @param maxRetries The maximum number of times a transaction will retry. - * @param timeout The maximum time a transaction will block for. - * @param trackReads Whether all reads should be tracked. Needed for blocking operations. - * @param writeSkew Whether writeskew is allowed. Disable with care. - * @param explicitRetries Whether explicit retries are allowed. - * @param interruptible Whether a blocking transaction can be interrupted. - * @param speculative Whether speculative configuration should be enabled. - * @param quickRelease Whether locks should be released as quickly as possible (before whole commit). - * @param traceLevel Transaction trace level. - * @param hooks Whether hooks for persistence modules and JTA should be added to the transaction. + * + *

familyName - Family name for transactions. Useful for debugging. + *

readonly - Sets transaction as readonly. Readonly transactions are cheaper. + *

maxRetries - The maximum number of times a transaction will retry. + *

timeout - The maximum time a transaction will block for. + *

trackReads - Whether all reads should be tracked. Needed for blocking operations. + *

writeSkew - Whether writeskew is allowed. Disable with care. + *

explicitRetries - Whether explicit retries are allowed. + *

interruptible - Whether a blocking transaction can be interrupted. + *

speculative - Whether speculative configuration should be enabled. + *

quickRelease - Whether locks should be released as quickly as possible (before whole commit). + *

traceLevel - Transaction trace level. + *

hooks - Whether hooks for persistence modules and JTA should be added to the transaction. */ class TransactionConfig(val familyName: String = TransactionConfig.FAMILY_NAME, val readonly: JBoolean = TransactionConfig.READONLY, @@ -91,24 +107,6 @@ object DefaultTransactionConfig extends TransactionConfig /** * Wrapper for transaction config, factory, and boilerplate. Used by atomic. - * Can be passed to atomic implicitly or explicitly. - *

- *

- * implicit val txFactory = TransactionFactory(readonly = true)
- * ...
- * atomic {
- *   // do something within a readonly transaction
- * }
- * 
- *

- * Can be created at different levels as needed. For example: as an implicit object - * used throughout a package, as a static implicit val within a singleton object and - * imported where needed, or as an implicit val within each instance of a class. - *

- * See TransactionConfig for configuration options. - *

- * If no explicit transactin factory is passed to atomic and there is no implicit - * transaction factory in scope, then a default transaction factory is used. */ object TransactionFactory { def apply(config: TransactionConfig) = new TransactionFactory(config) @@ -135,6 +133,24 @@ object TransactionFactory { /** * Wrapper for transaction config, factory, and boilerplate. Used by atomic. + * Can be passed to atomic implicitly or explicitly. + *

+ *

+ * implicit val txFactory = TransactionFactory(readonly = true)
+ * ...
+ * atomic {
+ *   // do something within a readonly transaction
+ * }
+ * 
+ *

+ * Can be created at different levels as needed. For example: as an implicit object + * used throughout a package, as a static implicit val within a singleton object and + * imported where needed, or as an implicit val within each instance of a class. + *

+ * If no explicit transaction factory is passed to atomic and there is no implicit + * transaction factory in scope, then a default transaction factory is used. + * + * @see TransactionConfig for configuration options. */ class TransactionFactory(val config: TransactionConfig = DefaultTransactionConfig, defaultName: String = TransactionConfig.FAMILY_NAME) { self => diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index 44cb76a6d3..5bc69c037d 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -119,7 +119,7 @@ class LocalStm extends TransactionManagement with Logging { /** * Global transaction management, global in the context of multiple threads. - * You this if you need to have one transaction span multiple threads (or Actors). + * Use this if you need to have one transaction span multiple threads (or Actors). *

* Example of atomic transaction management using the atomic block: *

@@ -169,8 +169,8 @@ trait StmUtil { def compensating[T](body: => T): Unit = MultiverseStmUtils.scheduleCompensatingTask(new Runnable { def run = body }) /** - * STM retry. Use within an atomic. - * Blocks the current transaction. Can be used to wait for a condition. + * STM retry for blocking transactions (use within an atomic). + * Can be used to wait for a condition. */ def retry = MultiverseStmUtils.retry diff --git a/akka-core/src/main/scala/stm/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala index e5b2ebaba5..be7b9c5189 100644 --- a/akka-core/src/main/scala/stm/TransactionalMap.scala +++ b/akka-core/src/main/scala/stm/TransactionalMap.scala @@ -17,7 +17,7 @@ object TransactionalMap { } /** - * TODO: documentation + * Transactional map that implements the mutable map interface with an underlying ref and hash map. * * @author Jonas Bonér */ diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala index e53bc0f362..e2ad6a2aeb 100644 --- a/akka-core/src/main/scala/stm/TransactionalVector.scala +++ b/akka-core/src/main/scala/stm/TransactionalVector.scala @@ -17,7 +17,7 @@ object TransactionalVector { } /** - * TODO: documentation + * Transactional vector that implements the indexed seq interface with an underlying ref and vector. * * @author Jonas Bonér */ diff --git a/akka-core/src/main/scala/util/Duration.scala b/akka-core/src/main/scala/util/Duration.scala index c2e08f68b4..f49e1ae04b 100644 --- a/akka-core/src/main/scala/util/Duration.scala +++ b/akka-core/src/main/scala/util/Duration.scala @@ -6,27 +6,6 @@ package se.scalablesolutions.akka.util import java.util.concurrent.TimeUnit -/** - * Utility for working with java.util.concurrent.TimeUnit durations. - *

- * Example: - *

- * import se.scalablesolutions.akka.util.Duration
- * import java.util.concurrent.TimeUnit
- * 
- * val duration = Duration(100, TimeUnit.MILLISECONDS)
- * val duration = Duration(100, "millis")
- * 
- * duration.toNanos
- * 
- *

- * Implicits are also provided for Int and Long. Example usage: - *

- * import se.scalablesolutions.akka.util.duration._
- *
- * val duration = 100.millis
- * 
- */ object Duration { def apply(length: Long, unit: TimeUnit) = new Duration(length, unit) def apply(length: Long, unit: String) = new Duration(length, timeUnit(unit)) @@ -39,6 +18,27 @@ object Duration { } } +/** + * Utility for working with java.util.concurrent.TimeUnit durations. + *

+ * Example: + *

+ * import se.scalablesolutions.akka.util.Duration
+ * import java.util.concurrent.TimeUnit
+ *
+ * val duration = Duration(100, TimeUnit.MILLISECONDS)
+ * val duration = Duration(100, "millis")
+ *
+ * duration.toNanos
+ * 
+ *

+ * Implicits are also provided for Int and Long. Example usage: + *

+ * import se.scalablesolutions.akka.util.duration._
+ *
+ * val duration = 100.millis
+ * 
+ */ class Duration(val length: Long, val unit: TimeUnit) { def toNanos = unit.toNanos(length) def toMicros = unit.toMicros(length) @@ -57,7 +57,7 @@ class DurationInt(n: Int) { def nanos = Duration(n, TimeUnit.NANOSECONDS) def nanosecond = Duration(n, TimeUnit.NANOSECONDS) def nano = Duration(n, TimeUnit.NANOSECONDS) - + def microseconds = Duration(n, TimeUnit.MICROSECONDS) def micros = Duration(n, TimeUnit.MICROSECONDS) def microsecond = Duration(n, TimeUnit.MICROSECONDS) @@ -77,7 +77,7 @@ class DurationLong(n: Long) { def nanos = Duration(n, TimeUnit.NANOSECONDS) def nanosecond = Duration(n, TimeUnit.NANOSECONDS) def nano = Duration(n, TimeUnit.NANOSECONDS) - + def microseconds = Duration(n, TimeUnit.MICROSECONDS) def micros = Duration(n, TimeUnit.MICROSECONDS) def microsecond = Duration(n, TimeUnit.MICROSECONDS) From 032a7b8faf09a01a6527d73a02104e43ed04eb2a Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Sun, 20 Jun 2010 17:15:17 +0200 Subject: [PATCH 31/38] Changed return type of CamelService.load to CamelService --- akka-camel/src/main/scala/CamelService.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 0e7fac4c9f..f689e6fe44 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -66,7 +66,10 @@ trait CamelService extends Bootable with Logging { * * @see onLoad */ - def load = onLoad + def load: CamelService = { + onLoad + this + } /** * Stops the CamelService. From be40cf65075cdc9a3de928f03219214e8ac7c996 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Sun, 20 Jun 2010 17:16:06 +0200 Subject: [PATCH 32/38] Added more examples to akka-sample-camel --- .../src/main/resources/context-standalone.xml | 2 +- .../src/main/scala/Boot.scala | 16 +++--- .../src/main/scala/ServerApplication.scala | 3 +- .../main/scala/StandaloneApplication.scala | 53 +++++++++++++++---- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml b/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml index a493678817..f0932ce1b3 100644 --- a/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml +++ b/akka-samples/akka-sample-camel/src/main/resources/context-standalone.xml @@ -7,6 +7,6 @@ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.akkasource.org/schema/akka http://scalablesolutions.se/akka/akka.xsd"> - + diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index f5335e5ecd..924cb6c9e5 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -27,14 +27,18 @@ class Boot { CamelContextManager.context.addRoutes(new CustomRouteBuilder) // ----------------------------------------------------------------------- - // Basic example (using a supervisor for consumer actors) + // Basic example // ----------------------------------------------------------------------- - val supervisor = Supervisor( - SupervisorConfig( - RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), - Supervise(actorOf[Consumer1], LifeCycle(Permanent)) :: - Supervise(actorOf[Consumer2], LifeCycle(Permanent)) :: Nil)) + actorOf[Consumer1].start + actorOf[Consumer2].start + + // Alternatively, use a supervisor for these actors + //val supervisor = Supervisor( + // SupervisorConfig( + // RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), + // Supervise(actorOf[Consumer1], LifeCycle(Permanent)) :: + // Supervise(actorOf[Consumer2], LifeCycle(Permanent)) :: Nil)) // ----------------------------------------------------------------------- // Routing example diff --git a/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala index 8f53bbb866..7d90e89720 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/ServerApplication.scala @@ -14,8 +14,7 @@ object ServerApplication { // def main(args: Array[String]) { - val camelService = CamelService.newInstance - camelService.load + val camelService = CamelService.newInstance.load RemoteNode.start("localhost", 7777) RemoteNode.register("remote2", actorOf[RemoteActor2].start) } diff --git a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala index ebfabe9ce2..aa4292696a 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -2,6 +2,8 @@ package sample.camel import org.apache.camel.impl.{DefaultCamelContext, SimpleRegistry} import org.apache.camel.builder.RouteBuilder +import org.apache.camel.spring.spi.ApplicationContextRegistry +import org.springframework.context.support.ClassPathXmlApplicationContext import se.scalablesolutions.akka.camel.{CamelService, CamelContextManager} import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject} @@ -9,7 +11,7 @@ import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject} /** * @author Martin Krasser */ -object PlainApplication { +object StandaloneApplication { def main(args: Array[String]) { import CamelContextManager.context @@ -20,19 +22,18 @@ object PlainApplication { // customize CamelContext CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new PlainApplicationRoute) + CamelContextManager.context.addRoutes(new StandaloneApplicationRoute) // start CamelService - val camelService = CamelService.newInstance - camelService.load + val camelService = CamelService.newInstance.load + + // access 'externally' registered active objects + assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test1", "msg1")) + assert("hello msg2" == context.createProducerTemplate.requestBody("direct:test2", "msg2")) // 'internally' register active object (requires CamelService) ActiveObject.newInstance(classOf[ConsumerPojo2]) - // access 'externally' registered active objects with active-object component - assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test1", "msg1")) - assert("hello msg2" == context.createProducerTemplate.requestBody("direct:test2", "msg2")) - // internal registration is done in background. Wait a bit ... Thread.sleep(1000) @@ -48,13 +49,43 @@ object PlainApplication { } } -class PlainApplicationRoute extends RouteBuilder { +class StandaloneApplicationRoute extends RouteBuilder { def configure = { + // routes to active objects (in SimpleRegistry) from("direct:test1").to("active-object:pojo1?method=foo") from("direct:test2").to("active-object:pojo2?method=foo") } } -object SpringApplication { - // TODO +object StandaloneSpringApplication { + def main(args: Array[String]) { + import CamelContextManager.context + + // use Spring application context as active object registry + val springctx = new ClassPathXmlApplicationContext("/context-standalone.xml") + val registry = new ApplicationContextRegistry(springctx) + + // customize CamelContext + CamelContextManager.init(new DefaultCamelContext(registry)) + CamelContextManager.context.addRoutes(new StandaloneSpringApplicationRoute) + + // start CamelService + val camelService = CamelService.newInstance.load + + // access 'externally' registered active objects with active-object component + assert("hello msg3" == context.createProducerTemplate.requestBody("direct:test3", "msg3")) + + // shutdown CamelService + camelService.unload + + // shutdown all (internally) created actors + ActorRegistry.shutdownAll + } } + +class StandaloneSpringApplicationRoute extends RouteBuilder { + def configure = { + // routes to active object (in ApplicationContextRegistry) + from("direct:test3").to("active-object:pojo3?method=foo") + } +} \ No newline at end of file From 0e70a5face287b58e9fa3c7e9e4c17d53f443421 Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 12:24:06 +0200 Subject: [PATCH 33/38] re #281: Added as[T] and asSilently[T] to Option[Any] via implicit conversions in object Actor. --- akka-core/src/main/scala/actor/Actor.scala | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 807355d95e..853ab79dd6 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -7,8 +7,9 @@ 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.util.Logging import se.scalablesolutions.akka.serialization.Serializer +import se.scalablesolutions.akka.util.Helpers.{ narrow, narrowSilently } +import se.scalablesolutions.akka.util.Logging import com.google.protobuf.Message @@ -279,8 +280,13 @@ object Actor extends Logging { case Spawn => body; self.stop } }).start ! Spawn - } + + /** + * Implicitly converts the given Option[Any] to a AnyOptionAsTypedOption which offers the method as[T] + * to convert an Option[Any] to an Option[T]. + */ + implicit def toAnyOptionAsTypedOption(anyOption: Option[Any]) = new AnyOptionAsTypedOption(anyOption) } /** @@ -496,3 +502,18 @@ trait Actor extends Logging { case Kill => throw new ActorKilledException("Actor [" + toString + "] was killed by a Kill message") } } + +private[actor] class AnyOptionAsTypedOption(anyOption: Option[Any]) { + + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will throw a ClassCastException + * if the actual type is not assignable from the given one. + */ + def as[T]: Option[T] = narrow[T](anyOption) + + /** + * Convenience helper to cast the given Option of Any to an Option of the given type. Will swallow a possible + * ClassCastException and return None in that case. + */ + def asSilently[T: Manifest]: Option[T] = narrowSilently[T](anyOption) +} From 88125a9bc2a5af86941a50bd92ddbace910a478d Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 12:25:24 +0200 Subject: [PATCH 34/38] re #281: Made akka-core compile and test after breaking changes introduced by removing the type parameter from ActorRef.!!. --- akka-core/src/main/scala/actor/ActiveObject.scala | 4 ++-- .../src/main/scala/dataflow/DataFlowVariable.scala | 3 +-- akka-core/src/main/scala/remote/RemoteServer.scala | 4 ++-- akka-core/src/test/scala/ActorPatternsTest.scala | 7 +++---- ...xecutorBasedEventDrivenDispatcherActorSpec.scala | 3 +-- akka-core/src/test/scala/InMemoryActorSpec.scala | 13 ++++++------- ...SingleThreadEventDrivenDispatcherActorSpec.scala | 3 +-- ...edThreadPoolEventDrivenDispatcherActorSpec.scala | 3 +-- akka-core/src/test/scala/StmSpec.scala | 13 ++++++------- akka-core/src/test/scala/ThreadBasedActorSpec.scala | 3 +-- 10 files changed, 24 insertions(+), 32 deletions(-) diff --git a/akka-core/src/main/scala/actor/ActiveObject.scala b/akka-core/src/main/scala/actor/ActiveObject.scala index f83e3f91ed..b66b4558d3 100644 --- a/akka-core/src/main/scala/actor/ActiveObject.scala +++ b/akka-core/src/main/scala/actor/ActiveObject.scala @@ -4,6 +4,7 @@ package se.scalablesolutions.akka.actor +import Actor._ import se.scalablesolutions.akka.config.FaultHandlingStrategy import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteRequestProtocol import se.scalablesolutions.akka.remote.{RemoteProtocolBuilder, RemoteClient, RemoteRequestProtocolIdFactory} @@ -11,7 +12,6 @@ import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, Completabl import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.serialization.Serializer import se.scalablesolutions.akka.util._ -import se.scalablesolutions.akka.util.Helpers.narrow import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint} import org.codehaus.aspectwerkz.proxy.Proxy @@ -549,7 +549,7 @@ private[akka] sealed class ActiveObjectAspect { actorRef ! Invocation(joinPoint, true, true, sender, senderFuture) null.asInstanceOf[AnyRef] } else { - val result = narrow[AnyRef](actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)) + val result = (actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)).as[AnyRef] if (result.isDefined) result.get else throw new IllegalStateException("No result defined for invocation [" + joinPoint + "]") } diff --git a/akka-core/src/main/scala/dataflow/DataFlowVariable.scala b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala index 42d9c593bd..baf3e33f6e 100644 --- a/akka-core/src/main/scala/dataflow/DataFlowVariable.scala +++ b/akka-core/src/main/scala/dataflow/DataFlowVariable.scala @@ -10,7 +10,6 @@ import java.util.concurrent.{ConcurrentLinkedQueue, LinkedBlockingQueue} import se.scalablesolutions.akka.actor.{Actor, ActorRef} import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.dispatch.CompletableFuture -import se.scalablesolutions.akka.util.Helpers.narrow /** * Implements Oz-style dataflow (single assignment) variables. @@ -103,7 +102,7 @@ import se.scalablesolutions.akka.dispatch.CompletableFuture else { val out = actorOf(new Out(this)).start blockedReaders.offer(out) - val result = narrow[T](out !! Get) + val result = (out !! Get).as[T] out ! Exit if (result.isDefined) result.get else throw new DataFlowVariableException("Timed out (after " + TIME_OUT + " milliseconds) while waiting for result") diff --git a/akka-core/src/main/scala/remote/RemoteServer.scala b/akka-core/src/main/scala/remote/RemoteServer.scala index 0749132ce2..54dfa4f075 100644 --- a/akka-core/src/main/scala/remote/RemoteServer.scala +++ b/akka-core/src/main/scala/remote/RemoteServer.scala @@ -10,8 +10,8 @@ import java.util.concurrent.{ConcurrentHashMap, Executors} import java.util.{Map => JMap} import se.scalablesolutions.akka.actor._ +import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.util._ -import se.scalablesolutions.akka.util.Helpers.narrow import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._ import se.scalablesolutions.akka.config.Config.config @@ -370,7 +370,7 @@ class RemoteServerHandler( if (request.getIsOneWay) actorRef.!(message)(sender) else { try { - val resultOrNone = narrow[AnyRef](actorRef.!!(message)(sender)) + val resultOrNone = (actorRef.!!(message)(sender)).as[AnyRef] val result = if (resultOrNone.isDefined) resultOrNone.get else null log.debug("Returning result from actor invocation [%s]", result) val replyBuilder = RemoteReplyProtocol.newBuilder diff --git a/akka-core/src/test/scala/ActorPatternsTest.scala b/akka-core/src/test/scala/ActorPatternsTest.scala index a130cc2d7c..f6205c2a91 100644 --- a/akka-core/src/test/scala/ActorPatternsTest.scala +++ b/akka-core/src/test/scala/ActorPatternsTest.scala @@ -5,7 +5,6 @@ import se.scalablesolutions.akka.config.ScalaConfig._ import se.scalablesolutions.akka.actor.Actor import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.util.Logging -import se.scalablesolutions.akka.util.Helpers.narrow import org.scalatest.Suite import org.junit.runner.RunWith @@ -40,9 +39,9 @@ class ActorPatternsTest extends junit.framework.TestCase with Suite with MustMat }.start val result = for { - a <- narrow[Int](d !! (testMsg1,5000)) - b <- narrow[Int](d !! (testMsg2,5000)) - c <- narrow[Int](d !! (testMsg3,5000)) + a <- (d !! (testMsg1,5000)).as[Int] + b <- (d !! (testMsg2,5000)).as[Int] + c <- (d !! (testMsg3,5000)).as[Int] } yield a + b + c result.get must be(21) diff --git a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala index 8084900de2..e679bc6b4f 100644 --- a/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ExecutorBasedEventDrivenDispatcherActorSpec.scala @@ -4,7 +4,6 @@ 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.util.Helpers.narrow import Actor._ object ExecutorBasedEventDrivenDispatcherActorSpec { @@ -42,7 +41,7 @@ class ExecutorBasedEventDrivenDispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result = narrow[String](actor !! ("Hello", 10000)) + val result = (actor !! ("Hello", 10000)).as[String] assert("World" === result.get) actor.stop } diff --git a/akka-core/src/test/scala/InMemoryActorSpec.scala b/akka-core/src/test/scala/InMemoryActorSpec.scala index 6700918169..fcc54399e2 100644 --- a/akka-core/src/test/scala/InMemoryActorSpec.scala +++ b/akka-core/src/test/scala/InMemoryActorSpec.scala @@ -5,7 +5,6 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.stm.{Ref, TransactionalMap, TransactionalVector} -import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object InMemoryActorSpec { @@ -117,7 +116,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetMapStateOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + val notifier = (stateful !! GetNotifier).as[CountDownLatch] assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("new state" === (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get) } @@ -139,7 +138,7 @@ class InMemoryActorSpec extends JUnitSuite { failer.start stateful ! SetMapStateOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + val notifier = (stateful !! GetNotifier).as[CountDownLatch] assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("init" === (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get) // check that state is == init state } @@ -164,7 +163,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetVectorStateOneWay("init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + val notifier = (stateful !! GetNotifier).as[CountDownLatch] assert(notifier.get.await(1, TimeUnit.SECONDS)) assert(2 === (stateful !! GetVectorSize).get) } @@ -187,7 +186,7 @@ class InMemoryActorSpec extends JUnitSuite { val failer = actorOf[InMemFailerActor] failer.start stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + val notifier = (stateful !! GetNotifier).as[CountDownLatch] assert(notifier.get.await(1, TimeUnit.SECONDS)) assert(1 === (stateful !! GetVectorSize).get) } @@ -212,7 +211,7 @@ class InMemoryActorSpec extends JUnitSuite { stateful.start stateful ! SetRefStateOneWay("init") // set init state stateful ! SuccessOneWay("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + val notifier = (stateful !! GetNotifier).as[CountDownLatch] assert(notifier.get.await(1, TimeUnit.SECONDS)) assert("new state" === (stateful !! GetRefState).get) } @@ -235,7 +234,7 @@ class InMemoryActorSpec extends JUnitSuite { val failer = actorOf[InMemFailerActor] failer.start stateful ! FailureOneWay("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method - val notifier = narrow[CountDownLatch](stateful !! GetNotifier) + 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 } diff --git a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala index 78afbe3438..996c410977 100644 --- a/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ReactorBasedSingleThreadEventDrivenDispatcherActorSpec.scala @@ -6,7 +6,6 @@ import org.junit.Test import Actor._ import se.scalablesolutions.akka.dispatch.Dispatchers -import se.scalablesolutions.akka.util.Helpers.narrow object ReactorBasedSingleThreadEventDrivenDispatcherActorSpec { class TestActor extends Actor { @@ -45,7 +44,7 @@ class ReactorBasedSingleThreadEventDrivenDispatcherActorSpec extends JUnitSuite @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result = narrow[String](actor !! ("Hello", 10000)).get + val result = (actor !! ("Hello", 10000)).as[String].get assert("World" === result) actor.stop } diff --git a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala b/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala index 94a93a9a95..b94a02a6fc 100644 --- a/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala +++ b/akka-core/src/test/scala/ReactorBasedThreadPoolEventDrivenDispatcherActorSpec.scala @@ -5,7 +5,6 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers -import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object ReactorBasedThreadPoolEventDrivenDispatcherActorSpec { @@ -40,7 +39,7 @@ class ReactorBasedThreadPoolEventDrivenDispatcherActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result = narrow[String](actor !! ("Hello", 10000)).get + val result = (actor !! ("Hello", 10000)).as[String].get assert("World" === result) actor.stop } diff --git a/akka-core/src/test/scala/StmSpec.scala b/akka-core/src/test/scala/StmSpec.scala index dddb616c60..1544936446 100644 --- a/akka-core/src/test/scala/StmSpec.scala +++ b/akka-core/src/test/scala/StmSpec.scala @@ -1,7 +1,6 @@ package se.scalablesolutions.akka.stm import se.scalablesolutions.akka.actor.{Actor, Transactor} -import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ import org.scalatest.Spec @@ -89,10 +88,10 @@ class StmSpec extends try { val actor = actorOf[GlobalTransactionVectorTestActor].start actor !! Add(5) - val size1: Int = narrow(actor !! Size).getOrElse(fail("Could not get Vector::size")) + val size1 = (actor !! Size).as[Int].getOrElse(fail("Could not get Vector::size")) size1 should equal(2) actor !! Add(2) - val size2 = narrow[Int](actor !! Size).getOrElse(fail("Could not get Vector::size")) + val size2 = (actor !! Size).as[Int].getOrElse(fail("Could not get Vector::size")) size2 should equal(3) } catch { case e => @@ -108,18 +107,18 @@ class StmSpec extends try { val actor = actorOf[NestedTransactorLevelOneActor].start actor !! Add(2) - val size1: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) + val size1 = (actor !! Size).as[Int].getOrElse(fail("Could not get size")) size1 should equal(2) actor !! Add(7) actor ! "HiLevelOne" - val size2: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) + val size2 = (actor !! Size).as[Int].getOrElse(fail("Could not get size")) size2 should equal(7) actor !! Add(0) actor ! "HiLevelTwo" - val size3: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) + val size3 = (actor !! Size).as[Int].getOrElse(fail("Could not get size")) size3 should equal(0) actor !! Add(3) - val size4: Int = narrow(actor !! Size).getOrElse(fail("Could not get size")) + val size4 = (actor !! Size).as[Int].getOrElse(fail("Could not get size")) size4 should equal(3) } catch { case e => diff --git a/akka-core/src/test/scala/ThreadBasedActorSpec.scala b/akka-core/src/test/scala/ThreadBasedActorSpec.scala index edbd1ca26d..eda6f4d52c 100644 --- a/akka-core/src/test/scala/ThreadBasedActorSpec.scala +++ b/akka-core/src/test/scala/ThreadBasedActorSpec.scala @@ -5,7 +5,6 @@ import org.scalatest.junit.JUnitSuite import org.junit.Test import se.scalablesolutions.akka.dispatch.Dispatchers -import se.scalablesolutions.akka.util.Helpers.narrow import Actor._ object ThreadBasedActorSpec { @@ -41,7 +40,7 @@ class ThreadBasedActorSpec extends JUnitSuite { @Test def shouldSendReplySync = { val actor = actorOf[TestActor].start - val result = narrow[String](actor !! ("Hello", 10000)) + val result = (actor !! ("Hello", 10000)).as[String] assert("World" === result.get) actor.stop } From 4bfde61e4ed297750761c2bf67520f2f4898c62a Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 12:47:23 +0200 Subject: [PATCH 35/38] re #281: Made all subprojects compile after breaking changes introduced by removing the type parameter from ActorRef.!!; test-compile still missing! --- akka-http/src/main/scala/Security.scala | 5 +++-- .../akka-sample-chat/src/main/scala/ChatServer.scala | 6 +++--- .../src/main/scala/SimpleService.scala | 6 +++--- .../akka-sample-security/src/main/scala/SimpleService.scala | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/akka-http/src/main/scala/Security.scala b/akka-http/src/main/scala/Security.scala index 72d79db3d9..284d82d98e 100644 --- a/akka-http/src/main/scala/Security.scala +++ b/akka-http/src/main/scala/Security.scala @@ -23,8 +23,9 @@ package se.scalablesolutions.akka.security import se.scalablesolutions.akka.actor.{Scheduler, Actor, ActorRef, ActorRegistry} -import se.scalablesolutions.akka.util.Logging +import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.config.Config +import se.scalablesolutions.akka.util.Logging import com.sun.jersey.api.model.AbstractMethod import com.sun.jersey.spi.container.{ResourceFilterFactory, ContainerRequest, ContainerRequestFilter, ContainerResponse, ContainerResponseFilter, ResourceFilter} @@ -87,7 +88,7 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging { override def filter(request: ContainerRequest): ContainerRequest = rolesAllowed match { case Some(roles) => { - val result : Option[AnyRef] = authenticator !! Authenticate(request, roles) + val result = (authenticator !! Authenticate(request, roles)).as[AnyRef] result match { case Some(OK) => request case Some(r) if r.isInstanceOf[Response] => diff --git a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala index abe76ebcfa..01e1738b87 100644 --- a/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala +++ b/akka-samples/akka-sample-chat/src/main/scala/ChatServer.scala @@ -63,10 +63,10 @@ case class ChatMessage(from: String, message: String) extends Event class ChatClient(val name: String) { val chat = RemoteClient.actorFor("chat:service", "localhost", 9999) - def login = chat ! Login(name) - def logout = chat ! Logout(name) + def login = chat ! Login(name) + def logout = chat ! Logout(name) def post(message: String) = chat ! ChatMessage(name, name + ": " + message) - def chatLog: ChatLog = (chat !! GetChatLog(name)).getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) + def chatLog = (chat !! GetChatLog(name)).as[ChatLog].getOrElse(throw new Exception("Couldn't get the chat log from ChatServer")) } /** diff --git a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala index 0a5af80a57..870739bf8f 100644 --- a/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-rest-scala/src/main/scala/SimpleService.scala @@ -54,7 +54,7 @@ class SimpleService { //Fetch the first actor of type SimpleServiceActor //Send it the "Tick" message and expect a NodeSeq back val result = for{a <- actorsFor(classOf[SimpleServiceActor]).headOption - r <- a.!![NodeSeq]("Tick")} yield r + r <- (a !! "Tick").as[NodeSeq]} yield r //Return either the resulting NodeSeq or a default one result getOrElse Error in counter } @@ -109,7 +109,7 @@ class PersistentSimpleService { //Fetch the first actor of type PersistentSimpleServiceActor //Send it the "Tick" message and expect a NodeSeq back val result = for{a <- actorsFor(classOf[PersistentSimpleServiceActor]).headOption - r <- a.!![NodeSeq]("Tick")} yield r + r <- (a !! "Tick").as[NodeSeq]} yield r //Return either the resulting NodeSeq or a default one result getOrElse Error in counter } @@ -156,7 +156,7 @@ class Chat { //Fetch the first actor of type ChatActor //Send it the "Tick" message and expect a NodeSeq back val result = for{a <- actorsFor(classOf[ChatActor]).headOption - r <- a.!![String](msg)} yield r + r <- (a !! msg).as[String]} yield r //Return either the resulting String or a default one result getOrElse "System__error" } diff --git a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala index 061555ef05..e5c8029eb8 100644 --- a/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala +++ b/akka-samples/akka-sample-security/src/main/scala/SimpleService.scala @@ -123,7 +123,7 @@ class SecureTickService { //Fetch the first actor of type PersistentSimpleServiceActor //Send it the "Tick" message and expect a NdeSeq back val result = for{a <- actorsFor(classOf[SecureTickActor]).headOption - r <- a.!![Integer]("Tick")} yield r + r <- (a !! "Tick").as[Integer]} yield r //Return either the resulting NodeSeq or a default one result match { case (Some(counter)) => (Tick: {counter}) From 15d1ded344236cc2d44590a1e063d71f205f6994 Mon Sep 17 00:00:00 2001 From: Johan Rask Date: Wed, 16 Jun 2010 11:24:47 +0200 Subject: [PATCH 36/38] Added support for scope and depdenency injection on target bean --- akka-spring/akka-spring-test-java/pom.xml | 17 +- .../main/resources/META-INF/spring.schemas | 2 +- .../akka/spring/akka-0.10.xsd | 242 ++++++++++++++++++ .../se/scalablesolutions/akka/spring/akka.xsd | 14 + .../main/scala/ActiveObjectFactoryBean.scala | 53 +++- .../src/main/scala/ActiveObjectParser.scala | 18 +- .../main/scala/ActiveObjectProperties.scala | 10 +- .../src/main/scala/AkkaBeansException.scala | 14 + .../scala/AkkaSpringConfigurationTags.scala | 5 + .../src/main/scala/PropertyEntries.scala | 18 ++ .../src/main/scala/PropertyEntry.scala | 17 ++ .../main/scala/SupervisionFactoryBean.scala | 2 +- ...ActiveObjectBeanDefinitionParserTest.scala | 13 +- .../scala/ActiveObjectFactoryBeanTest.scala | 24 ++ 14 files changed, 430 insertions(+), 19 deletions(-) create mode 100644 akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd create mode 100644 akka-spring/src/main/scala/AkkaBeansException.scala create mode 100644 akka-spring/src/main/scala/PropertyEntries.scala create mode 100644 akka-spring/src/main/scala/PropertyEntry.scala diff --git a/akka-spring/akka-spring-test-java/pom.xml b/akka-spring/akka-spring-test-java/pom.xml index 11a29262ac..ff36202071 100644 --- a/akka-spring/akka-spring-test-java/pom.xml +++ b/akka-spring/akka-spring-test-java/pom.xml @@ -17,6 +17,11 @@ + + akka + Akka Repo + http://www.scalablesolutions.se/akka/repository/ + project.embedded.module Project Embedded Repository @@ -146,23 +151,23 @@ se.scalablesolutions.akka - akka-core_2.8.0.Beta1 - 0.9 + akka-core_2.8.0.RC3 + 0.9.1 se.scalablesolutions.akka akka-util_2.8.0.Beta1 - 0.9 + 0.8.1 se.scalablesolutions.akka akka-util-java_2.8.0.Beta1 - 0.9 + 0.8.1 se.scalablesolutions.akka - akka-spring_2.8.0.Beta1 - 0.9 + akka-spring_2.8.0.RC3 + 0.9.1 org.springframework diff --git a/akka-spring/src/main/resources/META-INF/spring.schemas b/akka-spring/src/main/resources/META-INF/spring.schemas index d04d65566a..27f7704018 100644 --- a/akka-spring/src/main/resources/META-INF/spring.schemas +++ b/akka-spring/src/main/resources/META-INF/spring.schemas @@ -1 +1 @@ -http\://www.akkasource.org/schema/akka=se/scalablesolutions/akka/spring/akka.xsd +http\://scalablesolutions.se/akka/akka-0.10.xsd=se/scalablesolutions/akka/spring/akka-0.10.xsd diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd new file mode 100644 index 0000000000..c81fb12af3 --- /dev/null +++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka-0.10.xsd @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the remote host. + + + + + + + Port of the remote host. + + + + + + + + + + + Pre restart callback method that is called during restart. + + + + + + + Post restart callback method that is called during restart. + + + + + + + + + + + + + + + + + + + Name of the target class. + + + + + + + default timeout for '!!' invocations + + + + + + + Set to true if messages should have REQUIRES_NEW semantics + + + + + + + Interface implemented by target class. + + + + + + + Lifecycle, permanent or temporary + + + + + + + Supported scopes are singleton and prototype + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failover scheme, AllForOne or OneForOne + + + + + + + Maximal number of retries. + + + + + + + Timerange for restart. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd index 20cb966b8f..862cd06987 100644 --- a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd +++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd @@ -25,6 +25,13 @@ + + + + + + + @@ -158,6 +165,13 @@ Lifecycle, permanent or temporary + + + + + Supported scopes are singleton and prototype + + diff --git a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala index 66ef87a15d..b1093a9a08 100644 --- a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala +++ b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala @@ -4,19 +4,31 @@ package se.scalablesolutions.akka.spring +import java.beans.PropertyDescriptor + +import java.lang.reflect.Method +import org.springframework.beans.BeanWrapperImpl +import org.springframework.beans.BeanWrapper +import org.springframework.beans.BeanUtils +import org.springframework.util.ReflectionUtils +import org.springframework.util.StringUtils +import org.springframework.beans.factory.BeanFactory import org.springframework.beans.factory.config.AbstractFactoryBean import se.scalablesolutions.akka.actor.ActiveObject import reflect.BeanProperty import se.scalablesolutions.akka.config.ScalaConfig.RestartCallbacks import se.scalablesolutions.akka.dispatch.MessageDispatcher - +import se.scalablesolutions.akka.util.Logging /** * Factory bean for active objects. + * * @author michaelkober + * @author Johan Rask */ -class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] { +class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] with Logging { import StringReflect._ + import AkkaSpringConfigurationTags._ @BeanProperty var target: String = "" @BeanProperty var timeout: Long = _ @@ -28,6 +40,8 @@ class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] { @BeanProperty var port: Int = _ @BeanProperty var lifecycle: String = "" @BeanProperty var dispatcher: DispatcherProperties = _ + @BeanProperty var scope:String = VAL_SCOPE_SINGLETON + @BeanProperty var property:PropertyEntries = _ /* * @see org.springframework.beans.factory.FactoryBean#getObjectType() @@ -39,11 +53,44 @@ class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] { * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() */ def createInstance: AnyRef = { + if(scope.equals(VAL_SCOPE_SINGLETON)) { + setSingleton(true) + } else { + setSingleton(false) + } var argumentList = "" if (isRemote) argumentList += "r" if (hasInterface) argumentList += "i" if (hasDispatcher) argumentList += "d" - create(argumentList) + + setProperties( + create(argumentList)) +} + + /** + * This method manages element by injecting either + * values () and bean references () + */ + private def setProperties(ref:AnyRef) : AnyRef = { + log.debug("Processing properties and dependencies for target class %s",target) + val beanWrapper = new BeanWrapperImpl(ref); + for(entry <- property.entryList) { + val propertyDescriptor = BeanUtils.getPropertyDescriptor(ref.getClass,entry.name) + val method = propertyDescriptor.getWriteMethod(); + + if(StringUtils.hasText(entry.ref)) { + log.debug("Setting property %s with bean ref %s using method %s", + entry.name,entry.ref,method.getName) + method.invoke(ref,getBeanFactory().getBean(entry.ref)) + } else if(StringUtils.hasText(entry.value)) { + log.debug("Setting property %s with value %s using method %s", + entry.name,entry.value,method.getName) + beanWrapper.setPropertyValue(entry.name,entry.value) + } else { + throw new AkkaBeansException("Either property@ref or property@value must be set on property element") + } + } + ref } // TODO: check if this works in 2.8 (type inferred to Nothing instead of AnyRef here) diff --git a/akka-spring/src/main/scala/ActiveObjectParser.scala b/akka-spring/src/main/scala/ActiveObjectParser.scala index dd48f8dbe1..d3b25791c6 100644 --- a/akka-spring/src/main/scala/ActiveObjectParser.scala +++ b/akka-spring/src/main/scala/ActiveObjectParser.scala @@ -5,10 +5,12 @@ package se.scalablesolutions.akka.spring import org.springframework.util.xml.DomUtils import org.w3c.dom.Element +import scala.collection.JavaConversions._ /** * Parser trait for custom namespace configuration for active-object. * @author michaelkober + * @author Johan Rask */ trait ActiveObjectParser extends BeanParser with DispatcherParser { import AkkaSpringConfigurationTags._ @@ -23,6 +25,7 @@ trait ActiveObjectParser extends BeanParser with DispatcherParser { val remoteElement = DomUtils.getChildElementByTagName(element, REMOTE_TAG); val callbacksElement = DomUtils.getChildElementByTagName(element, RESTART_CALLBACKS_TAG); val dispatcherElement = DomUtils.getChildElementByTagName(element, DISPATCHER_TAG) + val propertyEntries = DomUtils.getChildElementsByTagName(element,PROPERTYENTRY_TAG) if (remoteElement != null) { objectProperties.host = mandatory(remoteElement, HOST) @@ -42,6 +45,14 @@ trait ActiveObjectParser extends BeanParser with DispatcherParser { objectProperties.dispatcher = dispatcherProperties } + for(element <- propertyEntries) { + val entry = new PropertyEntry() + entry.name = element.getAttribute("name"); + entry.value = element.getAttribute("value") + entry.ref = element.getAttribute("ref") + objectProperties.propertyEntries.add(entry) + } + try { objectProperties.timeout = mandatory(element, TIMEOUT).toLong } catch { @@ -58,8 +69,13 @@ trait ActiveObjectParser extends BeanParser with DispatcherParser { } if (!element.getAttribute(LIFECYCLE).isEmpty) { - objectProperties.lifecyclye = element.getAttribute(LIFECYCLE) + objectProperties.lifecycle = element.getAttribute(LIFECYCLE) } + + if (!element.getAttribute(SCOPE).isEmpty) { + objectProperties.scope = element.getAttribute(SCOPE) + } + objectProperties } diff --git a/akka-spring/src/main/scala/ActiveObjectProperties.scala b/akka-spring/src/main/scala/ActiveObjectProperties.scala index e273d27a8d..ba4828e2f9 100644 --- a/akka-spring/src/main/scala/ActiveObjectProperties.scala +++ b/akka-spring/src/main/scala/ActiveObjectProperties.scala @@ -20,8 +20,10 @@ class ActiveObjectProperties { var postRestart: String = "" var host: String = "" var port: Int = _ - var lifecyclye: String = "" + var lifecycle: String = "" + var scope:String = "" var dispatcher: DispatcherProperties = _ + var propertyEntries = new PropertyEntries() /** @@ -37,8 +39,10 @@ class ActiveObjectProperties { builder.addPropertyValue(TARGET, target) builder.addPropertyValue(INTERFACE, interface) builder.addPropertyValue(TRANSACTIONAL, transactional) - builder.addPropertyValue(LIFECYCLE, lifecyclye) + builder.addPropertyValue(LIFECYCLE, lifecycle) + builder.addPropertyValue(SCOPE, scope) builder.addPropertyValue(DISPATCHER_TAG, dispatcher) - } + builder.addPropertyValue(PROPERTYENTRY_TAG,propertyEntries) +} } diff --git a/akka-spring/src/main/scala/AkkaBeansException.scala b/akka-spring/src/main/scala/AkkaBeansException.scala new file mode 100644 index 0000000000..58928cf69e --- /dev/null +++ b/akka-spring/src/main/scala/AkkaBeansException.scala @@ -0,0 +1,14 @@ +package se.scalablesolutions.akka.spring + +import org.springframework.beans.BeansException + +/** +* Exception to use when something goes wrong during bean creation +@author Johan Rask +*/ +class AkkaBeansException(errorMsg:String,t:Throwable) extends BeansException(errorMsg,t) { + + def this(errorMsg:String) = { + this(errorMsg,null) + } +} \ No newline at end of file diff --git a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala index 8ceb1b8f6a..39cb09dd64 100644 --- a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala +++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala @@ -15,6 +15,7 @@ object AkkaSpringConfigurationTags { val ACTIVE_OBJECT_TAG = "active-object" val SUPERVISION_TAG = "supervision" val DISPATCHER_TAG = "dispatcher" + val PROPERTYENTRY_TAG = "property" // active-object sub tags val RESTART_CALLBACKS_TAG = "restart-callbacks" @@ -41,6 +42,7 @@ object AkkaSpringConfigurationTags { val PRE_RESTART = "pre" val POST_RESTART = "post" val LIFECYCLE = "lifecycle" + val SCOPE = "scope" // supervision attributes val FAILOVER = "failover" @@ -68,6 +70,9 @@ object AkkaSpringConfigurationTags { val VAL_LIFECYCYLE_TEMPORARY = "temporary" val VAL_LIFECYCYLE_PERMANENT = "permanent" + val VAL_SCOPE_SINGLETON = "singleton" + val VAL_SCOPE_PROTOTYPE = "prototype" + // Failover val VAL_ALL_FOR_ONE = "AllForOne" val VAL_ONE_FOR_ONE = "OneForOne" diff --git a/akka-spring/src/main/scala/PropertyEntries.scala b/akka-spring/src/main/scala/PropertyEntries.scala new file mode 100644 index 0000000000..935baac4f4 --- /dev/null +++ b/akka-spring/src/main/scala/PropertyEntries.scala @@ -0,0 +1,18 @@ +package se.scalablesolutions.akka.spring + +import org.springframework.beans.factory.support.BeanDefinitionBuilder + +import scala.collection.mutable._ + +/** +* Simple container for Properties +* @author Johan Rask +*/ +class PropertyEntries { + + var entryList:ListBuffer[PropertyEntry] = ListBuffer[PropertyEntry]() + + def add(entry:PropertyEntry) = { + entryList.append(entry) + } +} \ No newline at end of file diff --git a/akka-spring/src/main/scala/PropertyEntry.scala b/akka-spring/src/main/scala/PropertyEntry.scala new file mode 100644 index 0000000000..a01241635b --- /dev/null +++ b/akka-spring/src/main/scala/PropertyEntry.scala @@ -0,0 +1,17 @@ +package se.scalablesolutions.akka.spring + +/** +* Represents a property element +* @author Johan Rask +*/ +class PropertyEntry { + + var name:String = _ + var value:String = null + var ref:String = null + + + override def toString(): String = { + format("name = %s,value = %s, ref = %s", name,value,ref) + } +} \ No newline at end of file diff --git a/akka-spring/src/main/scala/SupervisionFactoryBean.scala b/akka-spring/src/main/scala/SupervisionFactoryBean.scala index d82d329f79..d8c44c3502 100644 --- a/akka-spring/src/main/scala/SupervisionFactoryBean.scala +++ b/akka-spring/src/main/scala/SupervisionFactoryBean.scala @@ -40,7 +40,7 @@ class SupervisionFactoryBean extends AbstractFactoryBean[ActiveObjectConfigurato */ private[akka] def createComponent(props: ActiveObjectProperties): Component = { import StringReflect._ - val lifeCycle = if (!props.lifecyclye.isEmpty && props.lifecyclye.equalsIgnoreCase(VAL_LIFECYCYLE_TEMPORARY)) new LifeCycle(new Temporary()) else new LifeCycle(new Permanent()) + val lifeCycle = if (!props.lifecycle.isEmpty && props.lifecycle.equalsIgnoreCase(VAL_LIFECYCYLE_TEMPORARY)) new LifeCycle(new Temporary()) else new LifeCycle(new Permanent()) val isRemote = (props.host != null) && (!props.host.isEmpty) val withInterface = (props.interface != null) && (!props.interface.isEmpty) if (isRemote) { diff --git a/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala index e7ed68c379..23972e8f2e 100644 --- a/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala +++ b/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala @@ -25,13 +25,18 @@ class ActiveObjectBeanDefinitionParserTest extends Spec with ShouldMatchers { val xml = + transactional="true" + scope="prototype"> + + val props = parser.parseActiveObject(dom(xml).getDocumentElement); assert(props != null) - assert(props.timeout == 1000) - assert(props.target == "foo.bar.MyPojo") + assert(props.timeout === 1000) + assert(props.target === "foo.bar.MyPojo") assert(props.transactional) + assert(props.scope === "prototype") + assert(props.propertyEntries.entryList.size === 1) } it("should throw IllegalArgumentException on missing mandatory attributes") { @@ -50,7 +55,7 @@ class ActiveObjectBeanDefinitionParserTest extends Spec with ShouldMatchers { val props = parser.parseActiveObject(dom(xml).getDocumentElement); assert(props != null) assert(props.dispatcher.dispatcherType == "thread-based") - } +} it("should parse remote ActiveObjects configuration") { val xml = Date: Mon, 21 Jun 2010 09:06:40 +0200 Subject: [PATCH 37/38] When interfaces are used, target instances are now created correctly --- .../src/main/scala/ActiveObjectFactoryBean.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala index b1093a9a08..b6a51138dc 100644 --- a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala +++ b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala @@ -110,15 +110,15 @@ class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] with Logging { if (argList == "r") { ActiveObject.newRemoteInstance(target.toClass, timeout, transactional, host, port, callbacks) } else if (argList == "ri" ) { - ActiveObject.newRemoteInstance(interface.toClass, target.toClass, timeout, transactional, host, port, callbacks) + ActiveObject.newRemoteInstance(interface.toClass, aNewInstance(target.toClass), timeout, transactional, host, port, callbacks) } else if (argList == "rd") { ActiveObject.newRemoteInstance(target.toClass, timeout, transactional, dispatcherInstance, host, port, callbacks) } else if (argList == "rid") { - ActiveObject.newRemoteInstance(interface.toClass, target.toClass, timeout, transactional, dispatcherInstance, host, port, callbacks) + ActiveObject.newRemoteInstance(interface.toClass, aNewInstance(target.toClass), timeout, transactional, dispatcherInstance, host, port, callbacks) } else if (argList == "i") { - ActiveObject.newInstance(interface.toClass, target.toClass, timeout, transactional, callbacks) + ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), timeout, transactional, callbacks) } else if (argList == "id") { - ActiveObject.newInstance(interface.toClass, target.toClass, timeout, transactional, dispatcherInstance, callbacks) + ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), timeout, transactional, dispatcherInstance, callbacks) } else if (argList == "d") { ActiveObject.newInstance(target.toClass, timeout, transactional, dispatcherInstance, callbacks) } else { @@ -126,6 +126,10 @@ class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] with Logging { } } + def aNewInstance[T <: AnyRef](clazz: Class[T]) : T = { + clazz.newInstance().asInstanceOf[T] + } + /** * create Option[RestartCallback] */ From f1d4c9af0bc068475af2e5872f1c87b9d4d1c406 Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 13:08:10 +0200 Subject: [PATCH 38/38] closes #281: Made all subprojects test after breaking changes introduced by removing the type parameter from ActorRef.!!. --- .../test/scala/CamelServiceFeatureTest.scala | 10 ++++----- .../src/test/scala/ProducerFeatureTest.scala | 4 ++-- .../src/test/scala/PublishRequestorTest.scala | 6 ++--- .../src/test/scala/RemoteConsumerTest.scala | 2 +- .../component/ActorComponentFeatureTest.scala | 4 ++-- .../scala/component/ActorProducerTest.scala | 2 +- akka-http/src/test/scala/SecuritySpec.scala | 6 ++--- .../scala/CassandraPersistentActorSpec.scala | 8 +++---- .../scala/RedisInconsistentSizeBugTest.scala | 4 ++-- .../test/scala/RedisPersistentActorSpec.scala | 6 ++--- .../src/test/scala/RedisPersistentQSpec.scala | 22 +++++++++---------- 11 files changed, 37 insertions(+), 37 deletions(-) diff --git a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala b/akka-camel/src/test/scala/CamelServiceFeatureTest.scala index fd57d83457..771ed83af3 100644 --- a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala +++ b/akka-camel/src/test/scala/CamelServiceFeatureTest.scala @@ -26,7 +26,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi // count expectations in the next step (needed for testing only). service.consumerPublisher.start // set expectations on publish count - val latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get // start the CamelService service.load // await publication of first test consumer @@ -43,7 +43,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi scenario("access registered consumer actors via Camel direct-endpoints") { given("two consumer actors registered before and after CamelService startup") - val latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get actorOf(new TestConsumer("direct:publish-test-2")).start assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -64,12 +64,12 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi given("a consumer actor that has been stopped") assert(CamelContextManager.context.hasEndpoint(endpointUri) eq null) - var latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(1)).get + var latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get val consumer = actorOf(new TestConsumer(endpointUri)).start assert(latch.await(5000, TimeUnit.MILLISECONDS)) assert(CamelContextManager.context.hasEndpoint(endpointUri) ne null) - latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(1)).get + latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get consumer.stop assert(latch.await(5000, TimeUnit.MILLISECONDS)) // endpoint is still there but the route has been stopped @@ -103,7 +103,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi scenario("access active object methods via Camel direct-endpoints") { given("an active object registered after CamelService startup") - val latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(3)).get + val latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get ActiveObject.newInstance(classOf[PojoBase]) assert(latch.await(5000, TimeUnit.MILLISECONDS)) diff --git a/akka-camel/src/test/scala/ProducerFeatureTest.scala b/akka-camel/src/test/scala/ProducerFeatureTest.scala index eda866e064..96d1b9eeef 100644 --- a/akka-camel/src/test/scala/ProducerFeatureTest.scala +++ b/akka-camel/src/test/scala/ProducerFeatureTest.scala @@ -68,7 +68,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before when("a fail message is sent to the producer") val message = Message("fail", Map(Message.MessageExchangeId -> "123")) - val result = producer.!![Failure](message) + val result = (producer !! message).as[Failure] then("the expected failure message should be returned including a correlation identifier") val expectedFailureText = result.get.cause.getMessage @@ -84,7 +84,7 @@ class ProducerFeatureTest extends FeatureSpec with BeforeAndAfterAll with Before when("a fail message is sent to the producer") val message = Message("fail", Map(Message.MessageExchangeId -> "123")) - val result = producer.!![Failure](message) + val result = (producer !! message).as[Failure] then("the expected failure message should be returned including a correlation identifier") val expectedFailureText = result.get.cause.getMessage diff --git a/akka-camel/src/test/scala/PublishRequestorTest.scala b/akka-camel/src/test/scala/PublishRequestorTest.scala index f3c9a899b2..7729e6eec6 100644 --- a/akka-camel/src/test/scala/PublishRequestorTest.scala +++ b/akka-camel/src/test/scala/PublishRequestorTest.scala @@ -34,7 +34,7 @@ class PublishRequestorTest extends JUnitSuite { @Test def shouldReceiveConsumerMethodRegisteredEvent = { val obj = ActiveObject.newInstance(classOf[PojoSingle]) val init = AspectInit(classOf[PojoSingle], null, None, 1000) - val latch = publisher.!![CountDownLatch](SetExpectedTestMessageCount(1)).get + val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get requestor ! AspectInitRegistered(obj, init) assert(latch.await(5000, TimeUnit.MILLISECONDS)) val event = (publisher !! GetRetainedMessage).get.asInstanceOf[ConsumerMethodRegistered] @@ -45,7 +45,7 @@ class PublishRequestorTest extends JUnitSuite { } @Test def shouldReceiveConsumerRegisteredEvent = { - val latch = publisher.!![CountDownLatch](SetExpectedTestMessageCount(1)).get + val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get requestor ! ActorRegistered(consumer) assert(latch.await(5000, TimeUnit.MILLISECONDS)) assert((publisher !! GetRetainedMessage) === @@ -53,7 +53,7 @@ class PublishRequestorTest extends JUnitSuite { } @Test def shouldReceiveConsumerUnregisteredEvent = { - val latch = publisher.!![CountDownLatch](SetExpectedTestMessageCount(1)).get + val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get requestor ! ActorUnregistered(consumer) assert(latch.await(5000, TimeUnit.MILLISECONDS)) assert((publisher !! GetRetainedMessage) === diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index e1a7842e0d..4e2aa59b24 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -45,7 +45,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = actorOf[RemoteConsumer].start when("remote consumer publication is triggered") - val latch = service.consumerPublisher.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get consumer !! "init" assert(latch.await(5000, TimeUnit.MILLISECONDS)) diff --git a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala index f73a2fcd3e..b7fd607f28 100644 --- a/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala +++ b/akka-camel/src/test/scala/component/ActorComponentFeatureTest.scala @@ -26,7 +26,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with scenario("one-way communication using actor id") { val actor = actorOf[Tester1].start - val latch = actor.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get template.sendBody("actor:%s" format actor.id, "Martin") assert(latch.await(5000, TimeUnit.MILLISECONDS)) val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] @@ -35,7 +35,7 @@ class ActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with scenario("one-way communication using actor uuid") { val actor = actorOf[Tester1].start - val latch = actor.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get template.sendBody("actor:uuid:%s" format actor.uuid, "Martin") assert(latch.await(5000, TimeUnit.MILLISECONDS)) val reply = (actor !! GetRetainedMessage).get.asInstanceOf[Message] diff --git a/akka-camel/src/test/scala/component/ActorProducerTest.scala b/akka-camel/src/test/scala/component/ActorProducerTest.scala index 419784681b..6840bf1c79 100644 --- a/akka-camel/src/test/scala/component/ActorProducerTest.scala +++ b/akka-camel/src/test/scala/component/ActorProducerTest.scala @@ -19,7 +19,7 @@ class ActorProducerTest extends JUnitSuite with BeforeAndAfterAll { @Test def shouldSendMessageToActor = { val actor = actorOf[Tester1].start - val latch = actor.!![CountDownLatch](SetExpectedMessageCount(1)).get + val latch = (actor !! SetExpectedMessageCount(1)).as[CountDownLatch].get val endpoint = mockEndpoint("actor:uuid:%s" format actor.uuid) val exchange = endpoint.createExchange(ExchangePattern.InOnly) exchange.getIn.setBody("Martin") diff --git a/akka-http/src/test/scala/SecuritySpec.scala b/akka-http/src/test/scala/SecuritySpec.scala index 2ee7596a25..6a3cf4f803 100644 --- a/akka-http/src/test/scala/SecuritySpec.scala +++ b/akka-http/src/test/scala/SecuritySpec.scala @@ -39,7 +39,7 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase @Test def testChallenge = { val req = mock[ContainerRequest] - val result: Response = (authenticator !! (Authenticate(req, List("foo")), 10000)).get + val result = (authenticator !! (Authenticate(req, List("foo")), 10000)).as[Response].get // the actor replies with a challenge for the browser result.getStatus must equal(Response.Status.UNAUTHORIZED.getStatusCode) @@ -54,7 +54,7 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase // fake a request authorization -> this will authorize the user when(req.isUserInRole("chef")).thenReturn(true) - val result: AnyRef = (authenticator !! (Authenticate(req, List("chef")), 10000)).get + val result = (authenticator !! (Authenticate(req, List("chef")), 10000)).as[AnyRef].get result must be(OK) // the authenticator must have set a security context @@ -68,7 +68,7 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase when(req.getHeaderValue("Authorization")).thenReturn("Basic " + new String(Base64.encode("foo:bar"))) when(req.isUserInRole("chef")).thenReturn(false) // this will deny access - val result: Response = (authenticator !! (Authenticate(req, List("chef")), 10000)).get + val result = (authenticator !! (Authenticate(req, List("chef")), 10000)).as[Response].get result.getStatus must equal(Response.Status.FORBIDDEN.getStatusCode) diff --git a/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala b/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala index 97419f3231..74673f2041 100644 --- a/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-cassandra/src/test/scala/CassandraPersistentActorSpec.scala @@ -80,7 +80,7 @@ class CassandraPersistentActorSpec extends JUnitSuite { stateful.start stateful !! SetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val result: Array[Byte] = (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).get + val result = (stateful !! GetMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess")).as[Array[Byte]].get assertEquals("new state", new String(result, 0, result.length, "UTF-8")) } @@ -95,7 +95,7 @@ class CassandraPersistentActorSpec extends JUnitSuite { stateful !! Failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method fail("should have thrown an exception") } catch {case e: RuntimeException => {}} - val result: Array[Byte] = (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).get + val result = (stateful !! GetMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure")).as[Array[Byte]].get assertEquals("init", new String(result, 0, result.length, "UTF-8")) // check that state is == init state } @@ -128,7 +128,7 @@ class CassandraPersistentActorSpec extends JUnitSuite { stateful.start stateful !! SetRefState("init") // set init state stateful !! Success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired - val result: Array[Byte] = (stateful !! GetRefState).get + val result = (stateful !! GetRefState).as[Array[Byte]].get assertEquals("new state", new String(result, 0, result.length, "UTF-8")) } @@ -143,7 +143,7 @@ class CassandraPersistentActorSpec extends JUnitSuite { stateful !! Failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method fail("should have thrown an exception") } catch {case e: RuntimeException => {}} - val result: Array[Byte] = (stateful !! GetRefState).get + val result = (stateful !! GetRefState).as[Array[Byte]].get assertEquals("init", new String(result, 0, result.length, "UTF-8")) // check that state is == init state } diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala index dde431628a..74d1e95cc9 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisInconsistentSizeBugTest.scala @@ -66,9 +66,9 @@ object Runner { def run { val proc = actorOf[RedisSampleStorage] proc.start - val i: Option[String] = proc !! SETFOO("debasish") + val i = (proc !! SETFOO("debasish")).as[String] println("i = " + i) - val ev: Option[Int] = proc !! GETFOO("debasish") + val ev = (proc !! GETFOO("debasish")).as[Int] println(ev) } } diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala index 18dd4ce94d..236519abd8 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentActorSpec.scala @@ -113,7 +113,7 @@ class RedisPersistentActorSpec extends JUnitSuite { bactor !! Debit("a-123", 8000, failer) assertEquals(BigInt(1000), (bactor !! Balance("a-123")).get) - val c: Int = (bactor !! LogSize).get + val c = (bactor !! LogSize).as[Int].get assertTrue(7 == c) } @@ -134,7 +134,7 @@ class RedisPersistentActorSpec extends JUnitSuite { assertEquals(BigInt(5000), (bactor !! Balance("a-123")).get) // should not count the failed one - val c: Int = (bactor !! LogSize).get + val c = (bactor !! LogSize).as[Int].get assertTrue(3 == c) } @@ -156,7 +156,7 @@ class RedisPersistentActorSpec extends JUnitSuite { assertEquals(BigInt(5000), (bactor !! (Balance("a-123"), 5000)).get) // should not count the failed one - val c: Int = (bactor !! LogSize).get + val c = (bactor !! LogSize).as[Int].get assertTrue(3 == c) } } diff --git a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentQSpec.scala b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentQSpec.scala index 6cdd192593..5522b00d45 100644 --- a/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentQSpec.scala +++ b/akka-persistence/akka-persistence-redis/src/test/scala/RedisPersistentQSpec.scala @@ -58,7 +58,7 @@ class RedisPersistentQSpec extends JUnitSuite { qa !! NQ("a-123") qa !! NQ("a-124") qa !! NQ("a-125") - val t: Int = (qa !! SZ).get + val t = (qa !! SZ).as[Int].get assertTrue(3 == t) } @@ -69,12 +69,12 @@ class RedisPersistentQSpec extends JUnitSuite { qa !! NQ("a-123") qa !! NQ("a-124") qa !! NQ("a-125") - val s: Int = (qa !! SZ).get + val s = (qa !! SZ).as[Int].get assertTrue(3 == s) assertEquals("a-123", (qa !! DQ).get) assertEquals("a-124", (qa !! DQ).get) assertEquals("a-125", (qa !! DQ).get) - val t: Int = (qa !! SZ).get + val t = (qa !! SZ).as[Int].get assertTrue(0 == t) } @@ -88,13 +88,13 @@ class RedisPersistentQSpec extends JUnitSuite { qa !! NQ("a-123") qa !! NQ("a-124") qa !! NQ("a-125") - val t: Int = (qa !! SZ).get + val t = (qa !! SZ).as[Int].get assertTrue(3 == t) assertEquals("a-123", (qa !! DQ).get) - val s: Int = (qa !! SZ).get + val s = (qa !! SZ).as[Int].get assertTrue(2 == s) qa !! MNDQ(List("a-126", "a-127"), 2, failer) - val u: Int = (qa !! SZ).get + val u = (qa !! SZ).as[Int].get assertTrue(2 == u) } @@ -110,25 +110,25 @@ class RedisPersistentQSpec extends JUnitSuite { qa !! NQ("a-124") qa !! NQ("a-125") - val t: Int = (qa !! SZ).get + val t = (qa !! SZ).as[Int].get assertTrue(3 == t) // dequeue 1 assertEquals("a-123", (qa !! DQ).get) // size == 2 - val s: Int = (qa !! SZ).get + val s = (qa !! SZ).as[Int].get assertTrue(2 == s) // enqueue 2, dequeue 2 => size == 2 qa !! MNDQ(List("a-126", "a-127"), 2, failer) - val u: Int = (qa !! SZ).get + val u = (qa !! SZ).as[Int].get assertTrue(2 == u) // enqueue 2 => size == 4 qa !! NQ("a-128") qa !! NQ("a-129") - val v: Int = (qa !! SZ).get + val v = (qa !! SZ).as[Int].get assertTrue(4 == v) // enqueue 1 => size 5 @@ -138,7 +138,7 @@ class RedisPersistentQSpec extends JUnitSuite { qa !! MNDQ(List("a-130"), 6, failer) } catch { case e: Exception => {} } - val w: Int = (qa !! SZ).get + val w = (qa !! SZ).as[Int].get assertTrue(4 == w) } }