From bcff7fd9fc1c3e89e6cd7003f7ebf1b802197848 Mon Sep 17 00:00:00 2001 From: Peter Vlugter Date: Mon, 16 Aug 2010 11:38:39 +1200 Subject: [PATCH] Some Java friendliness for STM --- akka-core/src/main/scala/stm/Ref.scala | 9 +- .../scala/stm/TransactionFactoryBuilder.scala | 84 ++++++++++++++++ .../scala/stm/TransactionManagement.scala | 95 +++---------------- .../src/main/scala/stm/TransactionalMap.scala | 10 +- .../main/scala/stm/TransactionalVector.scala | 10 +- .../src/main/scala/stm/global/Atomic.scala | 41 ++++++++ .../src/main/scala/stm/global/GlobalStm.scala | 53 +++++++++++ .../src/main/scala/stm/global/package.scala | 10 ++ .../src/main/scala/stm/local/Atomic.scala | 41 ++++++++ .../src/main/scala/stm/local/LocalStm.scala | 44 +++++++++ .../src/main/scala/stm/local/package.scala | 10 ++ .../{packages.scala => transactional.scala} | 23 +---- .../scalablesolutions/akka/stm/Address.java | 13 +++ .../akka/stm/CounterExample.java | 26 +++++ .../akka/stm/JavaStmTests.java | 91 ++++++++++++++++++ .../akka/stm/RefExample.java | 36 +++++++ .../akka/stm/StmExamples.java | 18 ++++ .../akka/stm/TransactionFactoryExample.java | 30 ++++++ .../akka/stm/TransactionalMapExample.java | 35 +++++++ .../akka/stm/TransactionalVectorExample.java | 34 +++++++ .../se/scalablesolutions/akka/stm/User.java | 13 +++ .../src/test/scala/stm/JavaStmSpec.scala | 5 + .../src/main/scala/Ants.scala | 2 +- 23 files changed, 611 insertions(+), 122 deletions(-) create mode 100644 akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala create mode 100644 akka-core/src/main/scala/stm/global/Atomic.scala create mode 100644 akka-core/src/main/scala/stm/global/GlobalStm.scala create mode 100644 akka-core/src/main/scala/stm/global/package.scala create mode 100644 akka-core/src/main/scala/stm/local/Atomic.scala create mode 100644 akka-core/src/main/scala/stm/local/LocalStm.scala create mode 100644 akka-core/src/main/scala/stm/local/package.scala rename akka-core/src/main/scala/stm/{packages.scala => transactional.scala} (68%) create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java create mode 100644 akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java create mode 100644 akka-core/src/test/scala/stm/JavaStmSpec.scala diff --git a/akka-core/src/main/scala/stm/Ref.scala b/akka-core/src/main/scala/stm/Ref.scala index d660de1377..b0ae15c957 100644 --- a/akka-core/src/main/scala/stm/Ref.scala +++ b/akka-core/src/main/scala/stm/Ref.scala @@ -16,7 +16,7 @@ import org.multiverse.transactional.refs.BasicRef object Ref { def apply[T]() = new Ref[T]() - def apply[T](initialValue: T) = new Ref[T](Some(initialValue)) + def apply[T](initialValue: T) = new Ref[T](initialValue) /** * An implicit conversion that converts a Ref to an Iterable value. @@ -29,13 +29,10 @@ object Ref { * * @author Jonas Bonér */ -class Ref[T](initialOpt: Option[T] = None) - extends BasicRef[T](initialOpt.getOrElse(null.asInstanceOf[T])) - with Transactional { - +class Ref[T](initialValue: T) extends BasicRef[T](initialValue) with Transactional { self => - def this() = this(None) // Java compatibility + def this() = this(null.asInstanceOf[T]) val uuid = UUID.newUuid.toString diff --git a/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala b/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala new file mode 100644 index 0000000000..a5045ca090 --- /dev/null +++ b/akka-core/src/main/scala/stm/TransactionFactoryBuilder.scala @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import java.lang.{Boolean => JBoolean} + +import se.scalablesolutions.akka.util.Duration + +import org.multiverse.api.TraceLevel + +/** + * For more easily creating TransactionConfig from Java. + */ +class TransactionConfigBuilder { + var familyName: String = TransactionConfig.FAMILY_NAME + var readonly: JBoolean = TransactionConfig.READONLY + var maxRetries: Int = TransactionConfig.MAX_RETRIES + var timeout: Duration = TransactionConfig.DefaultTimeout + var trackReads: JBoolean = TransactionConfig.TRACK_READS + var writeSkew: Boolean = TransactionConfig.WRITE_SKEW + var explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES + var interruptible: Boolean = TransactionConfig.INTERRUPTIBLE + var speculative: Boolean = TransactionConfig.SPECULATIVE + var quickRelease: Boolean = TransactionConfig.QUICK_RELEASE + var traceLevel: TraceLevel = TransactionConfig.TRACE_LEVEL + var hooks: Boolean = TransactionConfig.HOOKS + + def setFamilyName(familyName: String) = { this.familyName = familyName; this } + def setReadonly(readonly: JBoolean) = { this.readonly = readonly; this } + def setMaxRetries(maxRetries: Int) = { this.maxRetries = maxRetries; this } + def setTimeout(timeout: Duration) = { this.timeout = timeout; this } + def setTrackReads(trackReads: JBoolean) = { this.trackReads = trackReads; this } + def setWriteSkew(writeSkew: Boolean) = { this.writeSkew = writeSkew; this } + def setExplicitRetries(explicitRetries: Boolean) = { this.explicitRetries = explicitRetries; this } + def setInterruptible(interruptible: Boolean) = { this.interruptible = interruptible; this } + def setSpeculative(speculative: Boolean) = { this.speculative = speculative; this } + def setQuickRelease(quickRelease: Boolean) = { this.quickRelease = quickRelease; this } + def setTraceLevel(traceLevel: TraceLevel) = { this.traceLevel = traceLevel; this } + def setHooks(hooks: Boolean) = { this.hooks = hooks; this } + + def build = new TransactionConfig( + familyName, readonly, maxRetries, timeout, trackReads, writeSkew, + explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks) +} + +/** + * For more easily creating TransactionFactory from Java. + */ +class TransactionFactoryBuilder { + var familyName: String = TransactionConfig.FAMILY_NAME + var readonly: JBoolean = TransactionConfig.READONLY + var maxRetries: Int = TransactionConfig.MAX_RETRIES + var timeout: Duration = TransactionConfig.DefaultTimeout + var trackReads: JBoolean = TransactionConfig.TRACK_READS + var writeSkew: Boolean = TransactionConfig.WRITE_SKEW + var explicitRetries: Boolean = TransactionConfig.EXPLICIT_RETRIES + var interruptible: Boolean = TransactionConfig.INTERRUPTIBLE + var speculative: Boolean = TransactionConfig.SPECULATIVE + var quickRelease: Boolean = TransactionConfig.QUICK_RELEASE + var traceLevel: TraceLevel = TransactionConfig.TRACE_LEVEL + var hooks: Boolean = TransactionConfig.HOOKS + + def setFamilyName(familyName: String) = { this.familyName = familyName; this } + def setReadonly(readonly: JBoolean) = { this.readonly = readonly; this } + def setMaxRetries(maxRetries: Int) = { this.maxRetries = maxRetries; this } + def setTimeout(timeout: Duration) = { this.timeout = timeout; this } + def setTrackReads(trackReads: JBoolean) = { this.trackReads = trackReads; this } + def setWriteSkew(writeSkew: Boolean) = { this.writeSkew = writeSkew; this } + def setExplicitRetries(explicitRetries: Boolean) = { this.explicitRetries = explicitRetries; this } + def setInterruptible(interruptible: Boolean) = { this.interruptible = interruptible; this } + def setSpeculative(speculative: Boolean) = { this.speculative = speculative; this } + def setQuickRelease(quickRelease: Boolean) = { this.quickRelease = quickRelease; this } + def setTraceLevel(traceLevel: TraceLevel) = { this.traceLevel = traceLevel; this } + def setHooks(hooks: Boolean) = { this.hooks = hooks; this } + + def build = { + val config = new TransactionConfig( + familyName, readonly, maxRetries, timeout, trackReads, writeSkew, + explicitRetries, interruptible, speculative, quickRelease, traceLevel, hooks) + new TransactionFactory(config) + } +} diff --git a/akka-core/src/main/scala/stm/TransactionManagement.scala b/akka-core/src/main/scala/stm/TransactionManagement.scala index 83bb5f1323..6aa80066e2 100644 --- a/akka-core/src/main/scala/stm/TransactionManagement.scala +++ b/akka-core/src/main/scala/stm/TransactionManagement.scala @@ -4,16 +4,11 @@ package se.scalablesolutions.akka.stm -import se.scalablesolutions.akka.util.Logging - -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.TimeUnit - import org.multiverse.api.{StmUtils => MultiverseStmUtils} import org.multiverse.api.ThreadLocalTransaction._ import org.multiverse.api.{Transaction => MultiverseTransaction} import org.multiverse.commitbarriers.CountDownCommitBarrier -import org.multiverse.templates.{TransactionalCallable, OrElseTemplate} +import org.multiverse.templates.OrElseTemplate class TransactionSetAbortedException(msg: String) extends RuntimeException(msg) @@ -93,84 +88,7 @@ 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() - 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 - val result = body - log.trace("Committing local transaction [" + mtx + "]") - result - } - }) - } -} - -/** - * Global transaction management, global in the context of multiple threads. - * Use 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() - 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 global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]") - try { - txSet.tryJoinCommit( - mtx, - TransactionConfig.DefaultTimeout.length, - TransactionConfig.DefaultTimeout.unit) - // Need to catch IllegalStateException until we have fix in Multiverse, since it throws it by mistake - } catch { case e: IllegalStateException => {} } - result - } - }) - } -} - trait StmUtil { - /** * Schedule a deferred task on the thread local transaction (use within an atomic). * This is executed when the transaction commits. @@ -209,3 +127,14 @@ trait StmUtil { }.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/TransactionalMap.scala b/akka-core/src/main/scala/stm/TransactionalMap.scala index 54d3c59db6..d45396ad25 100644 --- a/akka-core/src/main/scala/stm/TransactionalMap.scala +++ b/akka-core/src/main/scala/stm/TransactionalMap.scala @@ -11,9 +11,9 @@ import se.scalablesolutions.akka.util.UUID import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction object TransactionalMap { - def apply[K, V]() = new TransactionalMap[K, V] + def apply[K, V]() = new TransactionalMap[K, V]() - def apply[K, V](pairs: (K, V)*) = new TransactionalMap(Some(HashMap(pairs: _*))) + def apply[K, V](pairs: (K, V)*) = new TransactionalMap(HashMap(pairs: _*)) } /** @@ -21,12 +21,12 @@ object TransactionalMap { * * @author Jonas Bonér */ -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 +class TransactionalMap[K, V](initialValue: HashMap[K, V]) extends Transactional with scala.collection.mutable.Map[K, V] { + def this() = this(HashMap[K, V]()) val uuid = UUID.newUuid.toString - protected[this] val ref = new Ref(initialOpt.orElse(Some(HashMap[K, V]()))) + private[this] val ref = Ref(initialValue) def -=(key: K) = { remove(key) diff --git a/akka-core/src/main/scala/stm/TransactionalVector.scala b/akka-core/src/main/scala/stm/TransactionalVector.scala index 585782d326..2beeeecef0 100644 --- a/akka-core/src/main/scala/stm/TransactionalVector.scala +++ b/akka-core/src/main/scala/stm/TransactionalVector.scala @@ -11,9 +11,9 @@ import se.scalablesolutions.akka.util.UUID import org.multiverse.api.ThreadLocalTransaction.getThreadLocalTransaction object TransactionalVector { - def apply[T]() = new TransactionalVector[T] + def apply[T]() = new TransactionalVector[T]() - def apply[T](elems: T*) = new TransactionalVector(Some(Vector(elems: _*))) + def apply[T](elems: T*) = new TransactionalVector(Vector(elems: _*)) } /** @@ -21,12 +21,12 @@ object TransactionalVector { * * @author Jonas Bonér */ -class TransactionalVector[T](initialOpt: Option[Vector[T]] = None) extends Transactional with IndexedSeq[T] { - def this() = this(None) // Java compatibility +class TransactionalVector[T](initialValue: Vector[T]) extends Transactional with IndexedSeq[T] { + def this() = this(Vector[T]()) val uuid = UUID.newUuid.toString - private[this] val ref = new Ref(initialOpt.orElse(Some(Vector[T]()))) + private[this] val ref = Ref(initialValue) def clear = ref.swap(Vector[T]()) diff --git a/akka-core/src/main/scala/stm/global/Atomic.scala b/akka-core/src/main/scala/stm/global/Atomic.scala new file mode 100644 index 0000000000..d5a92fe047 --- /dev/null +++ b/akka-core/src/main/scala/stm/global/Atomic.scala @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm.global + +/** + * Java-friendly atomic blocks. + *

+ * Example usage (in Java): + *

+ *

+ * import se.scalablesolutions.akka.stm.*;
+ * import se.scalablesolutions.akka.stm.global.Atomic;
+ *
+ * final Ref ref = new Ref(0);
+ *
+ * new Atomic() {
+ *     public Object atomically() {
+ *         return ref.set(1);
+ *     }
+ * }.execute();
+ *
+ * // To configure transactions pass a TransactionFactory
+ *
+ * TransactionFactory txFactory = new TransactionFactoryBuilder()
+ *     .setReadonly(true)
+ *     .build();
+ *
+ * Integer value = new Atomic(txFactory) {
+ *     public Integer atomically() {
+ *         return ref.get();
+ *     }
+ * }.execute();
+ * 
+ */ +abstract class Atomic[T](factory: TransactionFactory) { + def this() = this(DefaultGlobalTransactionFactory) + def atomically: T + def execute: T = atomic(factory)(atomically) +} diff --git a/akka-core/src/main/scala/stm/global/GlobalStm.scala b/akka-core/src/main/scala/stm/global/GlobalStm.scala new file mode 100644 index 0000000000..1fd53ffe51 --- /dev/null +++ b/akka-core/src/main/scala/stm/global/GlobalStm.scala @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.Logging + +import org.multiverse.api.{Transaction => MultiverseTransaction} +import org.multiverse.templates.TransactionalCallable + +/** + * Global transaction management, global in the context of multiple threads. + * Use 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() + 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 global transaction [" + mtx + "]\n\tand joining transaction set [" + txSet + "]") + try { + txSet.tryJoinCommit( + mtx, + TransactionConfig.DefaultTimeout.length, + TransactionConfig.DefaultTimeout.unit) + // Need to catch IllegalStateException until we have fix in Multiverse, since it throws it by mistake + } catch { case e: IllegalStateException => {} } + result + } + }) + } +} + diff --git a/akka-core/src/main/scala/stm/global/package.scala b/akka-core/src/main/scala/stm/global/package.scala new file mode 100644 index 0000000000..9b8a1b289e --- /dev/null +++ b/akka-core/src/main/scala/stm/global/package.scala @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +/** + * For easily importing global STM. + */ +package object global extends GlobalStm with StmUtil with StmCommon diff --git a/akka-core/src/main/scala/stm/local/Atomic.scala b/akka-core/src/main/scala/stm/local/Atomic.scala new file mode 100644 index 0000000000..c06f99ba99 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/Atomic.scala @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm.local + +/** + * Java-friendly atomic blocks. + *

+ * Example usage (in Java): + *

+ *

+ * import se.scalablesolutions.akka.stm.*;
+ * import se.scalablesolutions.akka.stm.local.Atomic;
+ *
+ * final Ref ref = new Ref(0);
+ *
+ * new Atomic() {
+ *     public Object atomically() {
+ *         return ref.set(1);
+ *     }
+ * }.execute();
+ *
+ * // To configure transactions pass a TransactionFactory
+ *
+ * TransactionFactory txFactory = new TransactionFactoryBuilder()
+ *     .setReadonly(true)
+ *     .build();
+ *
+ * Integer value = new Atomic(txFactory) {
+ *     public Integer atomically() {
+ *         return ref.get();
+ *     }
+ * }.execute();
+ * 
+ */ +abstract class Atomic[T](factory: TransactionFactory) { + def this() = this(DefaultLocalTransactionFactory) + def atomically: T + def execute: T = atomic(factory)(atomically) +} diff --git a/akka-core/src/main/scala/stm/local/LocalStm.scala b/akka-core/src/main/scala/stm/local/LocalStm.scala new file mode 100644 index 0000000000..477027aab2 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/LocalStm.scala @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +import se.scalablesolutions.akka.util.Logging + +import org.multiverse.api.{Transaction => MultiverseTransaction} +import org.multiverse.templates.TransactionalCallable + +/** + * 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() + 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 + val result = body + log.trace("Committing local transaction [" + mtx + "]") + result + } + }) + } +} diff --git a/akka-core/src/main/scala/stm/local/package.scala b/akka-core/src/main/scala/stm/local/package.scala new file mode 100644 index 0000000000..406d4880f6 --- /dev/null +++ b/akka-core/src/main/scala/stm/local/package.scala @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka.stm + +/** + * For easily importing local STM. + */ +package object local extends LocalStm with StmUtil with StmCommon diff --git a/akka-core/src/main/scala/stm/packages.scala b/akka-core/src/main/scala/stm/transactional.scala similarity index 68% rename from akka-core/src/main/scala/stm/packages.scala rename to akka-core/src/main/scala/stm/transactional.scala index cbb3ad4804..e00c7ef8e9 100644 --- a/akka-core/src/main/scala/stm/packages.scala +++ b/akka-core/src/main/scala/stm/transactional.scala @@ -5,28 +5,7 @@ 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 - -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 + * For importing the transactional datastructures, including the primitive refs * and transactional data structures from Multiverse. */ package object transactional { diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java new file mode 100644 index 0000000000..cb3057929f --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/Address.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.stm; + +public class Address { + private String location; + + public Address(String location) { + this.location = location; + } + + @Override public String toString() { + return "Address(" + location + ")"; + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java new file mode 100644 index 0000000000..57a9a07daa --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/CounterExample.java @@ -0,0 +1,26 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class CounterExample { + final static Ref ref = new Ref(0); + + public static int counter() { + return new Atomic() { + public Integer atomically() { + int inc = ref.get() + 1; + ref.set(inc); + return inc; + } + }.execute(); + } + + public static void main(String[] args) { + System.out.println(); + System.out.println("Counter example"); + System.out.println(); + System.out.println("counter 1: " + counter()); + System.out.println("counter 2: " + counter()); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java new file mode 100644 index 0000000000..7204013808 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/JavaStmTests.java @@ -0,0 +1,91 @@ +package se.scalablesolutions.akka.stm; + +import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.Before; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +import org.multiverse.api.ThreadLocalTransaction; +import org.multiverse.api.TransactionConfiguration; +import org.multiverse.api.exceptions.ReadonlyException; + +public class JavaStmTests { + + private Ref ref; + + private int getRefValue() { + return new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + } + + public int increment() { + return new Atomic() { + public Integer atomically() { + int inc = ref.get() + 1; + ref.set(inc); + return inc; + } + }.execute(); + } + + @Before public void initialise() { + ref = new Ref(0); + } + + @Test public void incrementRef() { + assertEquals(0, getRefValue()); + increment(); + increment(); + increment(); + assertEquals(3, getRefValue()); + } + + @Test public void failSetRef() { + assertEquals(0, getRefValue()); + try { + new Atomic() { + public Object atomically() { + ref.set(3); + throw new RuntimeException(); + } + }.execute(); + } catch(RuntimeException e) {} + assertEquals(0, getRefValue()); + } + + @Test public void configureTransaction() { + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + // get transaction config from multiverse + TransactionConfiguration config = new Atomic(txFactory) { + public TransactionConfiguration atomically() { + ref.get(); + return ThreadLocalTransaction.getThreadLocalTransaction().getConfiguration(); + } + }.execute(); + + assertEquals("example", config.getFamilyName()); + assertEquals(true, config.isReadonly()); + } + + @Test(expected=ReadonlyException.class) public void failReadonlyTransaction() { + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + new Atomic(txFactory) { + public Object atomically() { + return ref.set(3); + } + }.execute(); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java new file mode 100644 index 0000000000..f590524fd7 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/RefExample.java @@ -0,0 +1,36 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class RefExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("Ref example"); + System.out.println(); + + final Ref ref = new Ref(0); + + Integer value1 = new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + + System.out.println("value 1: " + value1); + + new Atomic() { + public Object atomically() { + return ref.set(5); + } + }.execute(); + + Integer value2 = new Atomic() { + public Integer atomically() { + return ref.get(); + } + }.execute(); + + System.out.println("value 2: " + value2); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java new file mode 100644 index 0000000000..a8526f2dd0 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/StmExamples.java @@ -0,0 +1,18 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.Ref; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class StmExamples { + public static void main(String[] args) { + System.out.println(); + System.out.println("STM examples"); + System.out.println(); + + CounterExample.main(args); + RefExample.main(args); + TransactionFactoryExample.main(args); + TransactionalMapExample.main(args); + TransactionalVectorExample.main(args); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java new file mode 100644 index 0000000000..00dd87b7c5 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionFactoryExample.java @@ -0,0 +1,30 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +import org.multiverse.api.ThreadLocalTransaction; +import org.multiverse.api.TransactionConfiguration; + +public class TransactionFactoryExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionFactory example"); + System.out.println(); + + TransactionFactory txFactory = new TransactionFactoryBuilder() + .setFamilyName("example") + .setReadonly(true) + .build(); + + new Atomic(txFactory) { + public Object atomically() { + // check config has been passed to multiverse + TransactionConfiguration config = ThreadLocalTransaction.getThreadLocalTransaction().getConfiguration(); + System.out.println("family name: " + config.getFamilyName()); + System.out.println("readonly: " + config.isReadonly()); + return null; + } + }.execute(); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java new file mode 100644 index 0000000000..7c4940c7a5 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalMapExample.java @@ -0,0 +1,35 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class TransactionalMapExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionalMap example"); + System.out.println(); + + final TransactionalMap users = new TransactionalMap(); + + // fill users map (in a transaction) + new Atomic() { + public Object atomically() { + users.put("bill", new User("bill")); + users.put("mary", new User("mary")); + users.put("john", new User("john")); + return null; + } + }.execute(); + + System.out.println("users: " + users); + + // access users map (in a transaction) + User user = new Atomic() { + public User atomically() { + return users.get("bill").get(); + } + }.execute(); + + System.out.println("user: " + user); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java new file mode 100644 index 0000000000..7274848beb --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/TransactionalVectorExample.java @@ -0,0 +1,34 @@ +package se.scalablesolutions.akka.stm; + +import se.scalablesolutions.akka.stm.*; +import se.scalablesolutions.akka.stm.local.Atomic; + +public class TransactionalVectorExample { + public static void main(String[] args) { + System.out.println(); + System.out.println("TransactionalVector example"); + System.out.println(); + + final TransactionalVector
addresses = new TransactionalVector
(); + + // fill addresses vector (in a transaction) + new Atomic() { + public Object atomically() { + addresses.add(new Address("somewhere")); + addresses.add(new Address("somewhere else")); + return null; + } + }.execute(); + + System.out.println("addresses: " + addresses); + + // access addresses vector (in a transaction) + Address address = new Atomic
() { + public Address atomically() { + return addresses.get(0); + } + }.execute(); + + System.out.println("address: " + address); + } +} diff --git a/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java b/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java new file mode 100644 index 0000000000..c9dc4b3723 --- /dev/null +++ b/akka-core/src/test/java/se/scalablesolutions/akka/stm/User.java @@ -0,0 +1,13 @@ +package se.scalablesolutions.akka.stm; + +public class User { + private String name; + + public User(String name) { + this.name = name; + } + + @Override public String toString() { + return "User(" + name + ")"; + } +} diff --git a/akka-core/src/test/scala/stm/JavaStmSpec.scala b/akka-core/src/test/scala/stm/JavaStmSpec.scala new file mode 100644 index 0000000000..70dcefd79e --- /dev/null +++ b/akka-core/src/test/scala/stm/JavaStmSpec.scala @@ -0,0 +1,5 @@ +package se.scalablesolutions.akka.stm + +import org.scalatest.junit.JUnitWrapperSuite + +class JavaStmSpec extends JUnitWrapperSuite("se.scalablesolutions.akka.stm.JavaStmTests", Thread.currentThread.getContextClassLoader) 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 1fb0dea693..0bf8dc4fdf 100644 --- a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala +++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala @@ -40,7 +40,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)) { +class Place(initCell: Cell = EmptyCell) extends Ref(initCell) { def cell: Cell = getOrElse(EmptyCell) def food: Int = cell.food def food(i: Int) = alter(_.addFood(i))