Index: trunk/src/jvm/clojure/lang/ITransactionManager.java
===================================================================
--- trunk/src/jvm/clojure/lang/ITransactionManager.java	Mon Dec 08 14:19:07 EST 2008
+++ trunk/src/jvm/clojure/lang/ITransactionManager.java	Mon Dec 08 14:19:07 EST 2008
@@ -0,0 +1,33 @@
+package clojure.lang;
+
+/**
+ * Interface for external transaction manager, allowing integration with the STM system
+ */
+public interface ITransactionManager {
+    /**
+     * Begin a transaction against the external transaction manager
+     * @throws Exception if anything goes wrong
+     */
+    void beginTransaction() throws Exception;
+
+    /**
+     * Commit the current transaction to an external transaction manager
+     * @throws Exception if anything goes wrong.
+     * If the exception represents a transaction manager rollback, isRollbackException should return true for the exception thrown.
+     */
+    void commitTransaction() throws Exception;
+
+    /**
+     * Rollback the current transaction for the external transaction manager
+     * @throws Exception if anything goes wrong
+     */
+    void rollbackTransaction() throws Exception;
+
+    /**
+     * Test whether a given exception represents rollback against this transaction manager.  If so, the STM system will know to
+     * rollback it's transaction, and retry
+     * @param exception the exception thrown
+     * @return true if the exception represents rollback, false otherwise
+     */
+    boolean isRollbackException(Exception exception);
+}
Index: trunk/src/clj/clojure/core.clj
===================================================================
--- trunk/src/clj/clojure/core.clj	(revision 1147)
+++ trunk/src/clj/clojure/core.clj	Mon Dec 08 14:37:20 EST 2008
@@ -1184,10 +1184,19 @@
   once, but any effects on Refs will be atomic."
   [flags-ignored-for-now & body]
   `(. clojure.lang.LockingTransaction
-      (runInTransaction (fn [] ~@body))))
+      (runInTransaction (fn [] ~@body) nil)))
 
+(defmacro sync-external
+  "transaction-flags => TBD, pass nil for now
+   transaction-manager => an external transaction manager to integrate with.  Must implement clojure.lang.ITransactionManager
 
+  EXPERIMENTAL Like sync, but also executes the body in the transactional scope of an external transaction manager."
+  [flags-ignored-for-now transaction-manager & body]
+  `(. clojure.lang.LockingTransaction
+      (runInTransaction (fn [] ~@body) ~transaction-manager)))
 
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fn stuff ;;;;;;;;;;;;;;;;
 
 
@@ -2753,6 +2762,14 @@
   [& exprs]
   `(sync nil ~@exprs))
 
+(defmacro dosync-external
+  "EXPERIMENTAL: Like dosync, but also runs the exprs in a transaction managed by
+  an external transaction manager.  Effects on both any Refs altered by the transaction and
+  any side-effects managed by the trancation manager will be atomic, and mutually consistent.
+  The external transaction manager must implement the clojure.lang.ITransactionManager interface"
+  [transaction-manager & exprs]
+  `(sync-external nil ~transaction-manager ~@exprs))
+
 (defmacro with-precision
   "Sets the precision and rounding mode to be used for BigDecimal operations.
 
Index: trunk/src/jvm/clojure/lang/LockingTransaction.java
===================================================================
--- trunk/src/jvm/clojure/lang/LockingTransaction.java	(revision 1147)
+++ trunk/src/jvm/clojure/lang/LockingTransaction.java	Mon Dec 08 14:49:19 EST 2008
@@ -102,6 +102,7 @@
 final HashMap<Ref, Object> vals = new HashMap<Ref, Object>();
 final HashSet<Ref> sets = new HashSet<Ref>();
 final TreeMap<Ref, ArrayList<CFn>> commutes = new TreeMap<Ref, ArrayList<CFn>>();
+ITransactionManager transactionManager = null;
 
 
 //returns the most recent val
@@ -188,13 +189,28 @@
 	return t;
 }
 
-static public Object runInTransaction(Callable fn) throws Exception{
+static public Object runInTransaction(Callable fn, ITransactionManager transactionManager) throws Exception{
 	LockingTransaction t = transaction.get();
 	if(t == null)
 		transaction.set(t = new LockingTransaction());
-
+    if(transactionManager!=null && t.transactionManager!=null && t.transactionManager!=transactionManager)
+    {
+        throw new Exception("Cannot open a transaction against an external transaction manager in the scope of a transaction against a different external transaction manager");
+    }
+    boolean hadTransactionManager = (t.transactionManager!=null);
+    if(transactionManager!=null)
+        {
+        t.transactionManager = transactionManager;
+        }
 	if(t.info != null)
+    {
+        if(!hadTransactionManager && transactionManager!=null)
+        {
+            // beginin an external transaction in the scope of an purely STM transaction.
+            transactionManager.beginTransaction();
+        }
 		return fn.call();
+    }
 
 	return t.run(fn);
 }
@@ -215,6 +231,10 @@
 				startTime = System.nanoTime();
 				}
 			info = new Info(RUNNING, startPoint);
+            if(transactionManager!=null)
+                {
+                transactionManager.beginTransaction();
+                }
 			ret = fn.call();
 			//make sure no one has killed us before this point, and can't from now on
 			if(info.status.compareAndSet(RUNNING, COMMITTING))
@@ -253,8 +273,11 @@
 					Ref ref = e.getKey();
 					ref.validate(ref.getValidator(), e.getValue());
 					}
-
+                if(transactionManager!=null)
+                    {
+                    transactionManager.commitTransaction();
+                    }
-				//at this point, all values calced, all refs to be written locked
+                    //at this point, all values calced, all refs to be written locked
 				//no more client code to be called
 				long msecs = System.currentTimeMillis();
 				long commitPoint = getCommitPoint();
@@ -285,8 +308,21 @@
 			}
 		catch(RetryEx retry)
 			{
+                if (transactionManager != null) {
+                    transactionManager.rollbackTransaction();
+                }
-			//eat this so we retry rather than fall out
+                //eat this so we retry rather than fall out
 			}
+		catch(Exception exception)
+			{
+                if(transactionManager!=null && transactionManager.isRollbackException(exception))
+                {
+                    //eat this so we retry rather than fall out
+                }  else
+                {
+                    throw exception;
+                }
+			}
 		finally
 			{
 			for(int k = locked.size() - 1; k >= 0; --k)
