+ */
+
+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))