[Mulgara-svn] r113 - in trunk/src/jar: resolver/java/org/mulgara/resolver resolver-spi/java/org/mulgara/resolver/spi resolver-spi/java/org/mulgara/resolver/view

andrae at mulgara.org andrae at mulgara.org
Thu Oct 26 13:56:44 UTC 2006


Author: andrae
Date: 2006-10-26 08:56:44 -0500 (Thu, 26 Oct 2006)
New Revision: 113

Added:
   trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionException.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java
Modified:
   trunk/src/jar/resolver-spi/java/org/mulgara/resolver/spi/LocalSession.java
   trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/SessionView.java
   trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/ViewMarker.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/LocalQuery.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/ModifyModelOperation.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/OperationContext.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/QueryOperation.java
Log:
The only methods left on DatabaseSession that are not directly involved in
implementing the Session interface are

1) Transaction Methods - which will be replaced by the new MulgaraTransaction*
classes.

2) clearcache and fineResolverFactory - The former will be migrated to
OperationContextXAResource; the latter will be merged into
DatabaseOperationContext.

This commit also imports the MulgaraTransaction* classes and support.  This code
has been fixed from the xafix-design to the extent required to allow it to
compile.  It has also had various lines commented out to isolate it from the
existing code - permitting the existing code to continue to run unaffected.



Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -17,6 +17,8 @@
 
 package org.mulgara.resolver;
 
+import org.mulgara.query.TuplesException;
+
 public abstract class AnswerOperation {
   // Should use enum here.
   public static final int OBJECT = 1;
@@ -31,7 +33,7 @@
   protected long longint;
   protected boolean bool;
 
-  public abstract void execute();
+  public abstract void execute() throws TuplesException;
 
   protected void returnObject(Object object) {
     returnType = OBJECT;

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -63,6 +63,8 @@
 import org.mulgara.resolver.spi.ResolverFactoryException;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.SecurityAdapter;
+import org.mulgara.resolver.spi.Statements;
+import org.mulgara.resolver.spi.SymbolicTransformation;
 import org.mulgara.resolver.spi.SymbolicTransformationContext;
 import org.mulgara.resolver.spi.SystemResolver;
 import org.mulgara.resolver.view.ViewMarker;
@@ -93,8 +95,12 @@
    * This is named after the class.
    */
   private static final Logger logger =
-    Logger.getLogger("DatabaseOperationContext.class,getName()");
+    Logger.getLogger(DatabaseOperationContext.class.getName());
 
+  /** Logger for {@link SymbolicTransformation} plugins. */
+  private static final Logger symbolicLogger =
+    Logger.getLogger(DatabaseOperationContext.class.getName() + "#symbolic");
+
   /**
    * The models from external resolvers which have been cached as temporary
    * models.
@@ -134,6 +140,8 @@
   private final ResolverFactory    temporaryResolverFactory;
   private final TransactionManager transactionManager;
   private final Set                outstandingAnswers;
+  /** Symbolic transformations this instance should apply. */
+  private final List symbolicTransformationList;
 
   //
   // Constructor
@@ -154,7 +162,8 @@
                            URI                temporaryModelTypeURI,
                            ResolverFactory    temporaryResolverFactory,
                            TransactionManager transactionManager,
-                           Set                outstandingAnswers)
+                           Set                outstandingAnswers,
+                           List               symbolicTransformationList)
   {
     assert cachedModelSet             != null;
     assert cachedResolverFactorySet   != null;
@@ -185,6 +194,7 @@
     // Note this is only temporary - we will be eliminating outstandingAnswers
     // before the end of the transaction fix.
     this.outstandingAnswers         = outstandingAnswers;
+    this.symbolicTransformationList = symbolicTransformationList;
   }
 
   //
@@ -631,7 +641,10 @@
    *
    * This method must be called within a transactional context.
    *
-   * @deprecated Will be made package-scope as soon as the View kludge is resolved.
+   * Will be made package-scope as soon as the View kludge is resolved.
+   *
+   * Deprecation warning removed to assist development.
+   *
    * @param constraint  a localized constraint
    * @return the tuples satisfying the <var>constraint</var>
    * @throws IllegalArgumentException if <var>constraint</var> is
@@ -788,7 +801,7 @@
 
     Answer result = null;
     try {
-      result = DatabaseSession.doQuery(databaseSession, systemResolver, query);
+      result = doQuery(systemResolver, query);
     } catch (Throwable th) {
       try {
         logger.warn("Inner Query failed", th);
@@ -831,7 +844,7 @@
     } else {
       outstandingAnswers.remove(answer);
       if (databaseSession.autoCommit && outstandingAnswers.isEmpty()) {
-        if (databaseSession.transaction != null) {
+        if (databaseSession.getTransaction() != null) {
           databaseSession.resumeTransactionalBlock();
         }
         databaseSession.endTransactionalBlock("Could not commit query");
@@ -855,7 +868,7 @@
     Tuples result = null;
     try {
       LocalQuery lq = (LocalQuery)localQuery.clone();
-      DatabaseSession.transform(databaseSession, lq);
+      transform(lq);
       result = lq.resolve();
       lq.close();
     } catch (Throwable th) {
@@ -882,4 +895,93 @@
       throw new QueryException("Failed to suspend Transaction");
     }
   }
+
+  protected void doModify(SystemResolver systemResolver, URI modelURI,
+      Statements statements, boolean insert) throws Throwable {
+    long model = systemResolver.localize(new URIReferenceImpl(modelURI));
+    model = getCanonicalModel(model);
+
+    // Make sure security adapters are satisfied
+    for (Iterator i = securityAdapterList.iterator(); i.hasNext(); ) {
+      SecurityAdapter securityAdapter = (SecurityAdapter) i.next();
+
+      // Lie to the user
+      if (!securityAdapter.canSeeModel(model, systemResolver)) {
+        throw new QueryException("No such model " + modelURI);
+      }
+
+      // Tell the truth to the user
+      if (!securityAdapter.canModifyModel(model, systemResolver)) {
+        throw new QueryException("You aren't allowed to modify " + modelURI);
+      }
+    }
+
+    // Obtain a resolver for the destination model type
+    Resolver resolver = obtainResolver(findModelResolverFactory(model), systemResolver);
+    assert resolver != null;
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Modifying " + modelURI + " using " + resolver);
+    }
+
+    resolver.modifyModel(model, statements, insert);
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Modified " + modelURI);
+    }
+  }
+
+  public Answer doQuery(SystemResolver systemResolver, Query query) throws Exception
+  {
+    Answer result;
+
+    LocalQuery localQuery = new LocalQuery(query, systemResolver, this);
+
+    transform(localQuery);
+
+    // Complete the numerical phase of resolution
+    Tuples tuples = localQuery.resolve();
+    result = new SubqueryAnswer(this, systemResolver, tuples, query.getVariableList());
+    tuples.close();
+    localQuery.close();
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Answer rows = " + result.getRowCount());
+    }
+    return result;
+  }
+
+  /**
+   *
+   * Perform in-place transformation of localQuery.
+   * Note: we really want to convert this to a functional form eventually.
+   */
+  void transform(LocalQuery localQuery) throws Exception {
+    // Start with the symbolic phase of resolution
+    LocalQuery.MutableLocalQueryImpl mutableLocalQueryImpl =
+      localQuery.new MutableLocalQueryImpl();
+    if (symbolicLogger.isDebugEnabled()) {
+      symbolicLogger.debug("Before transformation: " + mutableLocalQueryImpl);
+    }
+    Iterator i = symbolicTransformationList.iterator();
+    while (i.hasNext()) {
+      SymbolicTransformation symbolicTransformation =
+        (SymbolicTransformation) i.next();
+      assert symbolicTransformation != null;
+      symbolicTransformation.transform(this, mutableLocalQueryImpl);
+      if (mutableLocalQueryImpl.isModified()) {
+        // When a transformation succeeds, we rewind and start from the
+        // beginning of the symbolicTransformationList again
+        if (symbolicLogger.isDebugEnabled()) {
+          symbolicLogger.debug("Symbolic transformation: " +
+                               mutableLocalQueryImpl);
+        }
+        mutableLocalQueryImpl.close();
+        mutableLocalQueryImpl = localQuery.new MutableLocalQueryImpl();
+        i = symbolicTransformationList.iterator();
+      }
+    }
+    mutableLocalQueryImpl.close();
+  }
+
 }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -86,10 +86,6 @@
   private static final Logger logger =
     Logger.getLogger(DatabaseSession.class.getName());
 
-  /** Logger for {@link SymbolicTransformation} plugins. */
-  private static final Logger symbolicLogger =
-    Logger.getLogger(DatabaseSession.class.getName() + "#symbolic");
-
   private static DatabaseSession writeSession = null;
   private boolean writing;
 
@@ -161,7 +157,7 @@
   private final TransactionManager transactionManager;
 
   /** Session transaction */
-  Transaction transaction;
+  private Transaction transaction;
 
   /** The name of the rule loader to use */
   private String ruleLoaderClassName;
@@ -337,7 +333,8 @@
                                         temporaryModelTypeURI,
                                         temporaryResolverFactory,
                                         transactionManager,
-                                        outstandingAnswers
+                                        outstandingAnswers,
+                                        symbolicTransformationList
                                       );
 
     if (logger.isDebugEnabled()) {
@@ -352,6 +349,36 @@
     }
   }
 
+
+  /**
+   * Non-rule version of the constructor.  Accepts all parameters except ruleLoaderClassName.
+   */
+  DatabaseSession(TransactionManager transactionManager,
+      List securityAdapterList,
+      List symbolicTransformationList,
+      ResolverSessionFactory resolverSessionFactory,
+      SystemResolverFactory systemResolverFactory,
+      ResolverFactory temporaryResolverFactory,
+      List resolverFactoryList,
+      Map externalResolverFactoryMap,
+      Map internalResolverFactoryMap,
+      DatabaseMetadata metadata,
+      ContentHandlerManager contentHandlers,
+      Set cachedResolverFactorySet,
+      URI temporaryModelTypeURI) throws ResolverFactoryException {
+    this(transactionManager, securityAdapterList, symbolicTransformationList, resolverSessionFactory,
+        systemResolverFactory, temporaryResolverFactory, resolverFactoryList, externalResolverFactoryMap,
+        internalResolverFactoryMap, metadata, contentHandlers, cachedResolverFactorySet,
+        temporaryModelTypeURI, "");
+  }
+
+  //
+  // Internal methods required for database initialisation.
+  //
+
+  /**
+   * Used by Database *only* to bootstrap the system model on DB startup.
+   */
   long bootstrapSystemModel(DatabaseMetadataImpl metadata) throws
       QueryException {
     logger.info("Bootstrapping System Model");
@@ -416,34 +443,7 @@
     }
   }
 
-
   /**
-   * Non-rule version of the constructor.  Accepts all parameters except ruleLoaderClassName.
-   */
-  DatabaseSession(TransactionManager transactionManager,
-      List securityAdapterList,
-      List symbolicTransformationList,
-      ResolverSessionFactory resolverSessionFactory,
-      SystemResolverFactory systemResolverFactory,
-      ResolverFactory temporaryResolverFactory,
-      List resolverFactoryList,
-      Map externalResolverFactoryMap,
-      Map internalResolverFactoryMap,
-      DatabaseMetadata metadata,
-      ContentHandlerManager contentHandlers,
-      Set cachedResolverFactorySet,
-      URI temporaryModelTypeURI) throws ResolverFactoryException {
-    this(transactionManager, securityAdapterList, symbolicTransformationList, resolverSessionFactory,
-        systemResolverFactory, temporaryResolverFactory, resolverFactoryList, externalResolverFactoryMap,
-        internalResolverFactoryMap, metadata, contentHandlers, cachedResolverFactorySet,
-        temporaryModelTypeURI, "");
-  }
-
-  public ResolverSession getResolverSession() {
-    return systemResolver;
-  }
-
-  /**
    * Preallocate a local node number for an RDF {@link Node}.
    *
    * This method is used only by {@link DatabaseResolverFactoryInitializer}
@@ -465,6 +465,22 @@
   // Methods implementing Session
   //
 
+  public void insert(URI modelURI, Set statements) throws QueryException {
+    modify(modelURI, statements, ASSERT_STATEMENTS);
+  }
+
+  public void insert(URI modelURI, Query query) throws QueryException {
+    modify(modelURI, query, ASSERT_STATEMENTS);
+  }
+
+  public void delete(URI modelURI, Set statements) throws QueryException {
+    modify(modelURI, statements, DENY_STATEMENTS);
+  }
+
+  public void delete(URI modelURI, Query query) throws QueryException {
+    modify(modelURI, query, DENY_STATEMENTS);
+  }
+
   /**
    * Backup all the data on the specified server. The database is not changed by
    * this method.
@@ -485,67 +501,67 @@
    * @param outputStream The stream to receive the contents
    * @throws QueryException if the backup cannot be completed.
    */
-  public void backup(URI sourceURI,
-      OutputStream outputStream) throws QueryException {
+  public void backup(URI sourceURI, OutputStream outputStream) throws QueryException {
     this.backup(outputStream, sourceURI, null);
   }
 
   /**
-   * Backup all the data on the specified server to a URI or an output stream.
-   * The database is not changed by this method.
+   * Restore all the data on the specified server. If the database is not
+   * currently empty then the database will contain the union of its current
+   * content and the content of the backup file when this method returns.
    *
-   * If an outputstream is supplied then the destinationURI is ignored.
+   * @param serverURI The URI of the server to restore.
+   * @param sourceURI The URI of the backup file to restore from.
+   * @throws QueryException if the restore cannot be completed.
+   */
+  public void restore(URI serverURI, URI sourceURI) throws QueryException {
+    this.restore(null, serverURI, sourceURI);
+  }
+
+  /**
+   * Restore all the data on the specified server. If the database is not
+   * currently empty then the database will contain the union of its current
+   * content and the content of the backup file when this method returns.
    *
-   * @param outputStream Optional output stream to receive the contents
-   * @param serverURI The URI of the server to backup.
-   * @param destinationURI Option URI of the file to backup into.
-   * @throws QueryException if the backup cannot be completed.
+   * @param inputStream a client supplied inputStream to obtain the restore
+   *        content from. If null assume the sourceURI has been supplied.
+   * @param serverURI The URI of the server to restore.
+   * @param sourceURI The URI of the backup file to restore from.
+   * @throws QueryException if the restore cannot be completed.
    */
-  private synchronized void backup(OutputStream outputStream,
-      URI serverURI,
-      URI destinationURI) throws QueryException {
-    execute(
-        new BackupOperation(outputStream, serverURI, destinationURI),
-        "Unable to backup to " + destinationURI
-        );
+  public void restore(InputStream inputStream, URI serverURI,
+      URI sourceURI) throws QueryException {
+    execute(new RestoreOperation(inputStream, serverURI, sourceURI),
+            "Unable to restore from " + sourceURI);
   }
 
-  public void close() throws QueryException {
-    logger.info("Closing session");
-    if (!autoCommit) {
-      logger.warn("Closing session while holding write-lock");
-
-      try {
-        resumeTransactionalBlock();
-      } catch (Throwable th) {
-        releaseWriteLock();
-        throw new QueryException("Error while resuming transaction in close", th);
-      }
-
-      try {
-        rollbackTransactionalBlock(
-            new QueryException("Attempt to close session whilst in transaction"));
-      } finally {
-        endTransactionalBlock("Failed to release write-lock in close");
-      }
-    } else {
-      if (this.transaction != null) {
-        resumeTransactionalBlock();
-        endPreviousQueryTransaction();
-      }
+  public Answer query(Query query) throws QueryException {
+    if (logger.isInfoEnabled()) {
+      logger.info("QUERY: " + query);
     }
+
+    // Evaluate the query
+    QueryOperation queryOperation = new QueryOperation(query, this);
+    executeQuery(queryOperation);
+    return queryOperation.getAnswer();
   }
 
-  public void commit() throws QueryException {
-    logger.info("Committing transaction");
-    if (!autoCommit) {
-      synchronized (DatabaseSession.class) {
-        setAutoCommit(true);
-        setAutoCommit(false);
+  public List query(List queryList) throws QueryException {
+
+    if (logger.isInfoEnabled()) {
+      StringBuffer log = new StringBuffer("QUERYING LIST: ");
+      for (int i = 0; i < queryList.size(); i++) {
+        log.append(queryList.get(i));
       }
+      logger.info(log.toString());
     }
+
+    QueryOperation queryOperation = new QueryOperation(queryList, this);
+    executeQuery(queryOperation);
+    return queryOperation.getAnswerList();
   }
 
+
   public void createModel(URI modelURI, URI modelTypeURI) throws QueryException {
     if (logger.isInfoEnabled()) {
       logger.info("Creating Model " + modelURI + " with type " + modelTypeURI);
@@ -556,231 +572,84 @@
               modelTypeURI);
   }
 
-  public void delete(URI modelURI, Set statements) throws QueryException {
-    modify(modelURI, statements, DENY_STATEMENTS);
-  }
-
-  public void delete(URI modelURI, Query query) throws QueryException {
-    modify(modelURI, query, DENY_STATEMENTS);
-  }
-
-  public void insert(URI modelURI, Set statements) throws QueryException {
-    modify(modelURI, statements, ASSERT_STATEMENTS);
-  }
-
-  public void insert(URI modelURI, Query query) throws QueryException {
-    modify(modelURI, query, ASSERT_STATEMENTS);
-  }
-
-  protected void modify(URI modelURI, Set statements, boolean insert)
-    throws QueryException
-  {
+  public void removeModel(URI modelURI) throws QueryException {
     if (logger.isInfoEnabled()) {
-      logger.info("Inserting statements into " + modelURI);
+      logger.info("REMOVE MODEL: " + modelURI);
     }
-    if (logger.isDebugEnabled()) {
-      logger.debug("Inserting statements: " + statements);
+    // Validate "modelURI" parameter
+    if (modelURI == null) {
+      throw new IllegalArgumentException("Null \"modelURI\" parameter");
     }
 
-    execute(new ModifyModelOperation(modelURI, statements, insert),
-            "Could not commit insert");
+    execute(new RemoveModelOperation(modelURI), "Unable to remove " + modelURI);
   }
 
-  private void modify(URI modelURI, Query query,
-      boolean insert) throws QueryException {
-    if (logger.isInfoEnabled()) {
-      logger.info("INSERT QUERY: " + query + " into " + modelURI);
-    }
+  public boolean modelExists(URI modelURI) throws QueryException {
+    ModelExistsOperation operation = new ModelExistsOperation(modelURI);
 
-    execute(new ModifyModelOperation(modelURI, query, insert, this),
-            "Unable to modify " + modelURI);
-  }
+    execute(operation, "Failed to determine model existence");
 
-  protected void doModify(SystemResolver systemResolver, URI modelURI,
-      Statements statements, boolean insert) throws Throwable {
-    long model = systemResolver.localize(new URIReferenceImpl(modelURI));
-    model = operationContext.getCanonicalModel(model);
-
-    // Make sure security adapters are satisfied
-    for (Iterator i = securityAdapterList.iterator(); i.hasNext(); ) {
-      SecurityAdapter securityAdapter = (SecurityAdapter) i.next();
-
-      // Lie to the user
-      if (!securityAdapter.canSeeModel(model, systemResolver)) {
-        throw new QueryException("No such model " + modelURI);
-      }
-
-      // Tell the truth to the user
-      if (!securityAdapter.canModifyModel(model, systemResolver)) {
-        throw new QueryException("You aren't allowed to modify " + modelURI);
-      }
-    }
-
-    // Obtain a resolver for the destination model type
-    Resolver resolver = operationContext.obtainResolver(
-                          operationContext.findModelResolverFactory(model),
-                          systemResolver
-                        );
-    assert resolver != null;
-
-    if (logger.isDebugEnabled()) {
-      logger.debug("Modifying " + modelURI + " using " + resolver);
-    }
-
-    resolver.modifyModel(model, statements, insert);
-
-    if (logger.isDebugEnabled()) {
-      logger.debug("Modified " + modelURI);
-    }
+    return operation.getResult();
   }
 
-  public void login(URI securityDomain, String user, char[] password) {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Login of " + user + " to " + securityDomain);
-    }
-
-    /*
-    execute(new LoginOperation(securityDomain, user, password),
-            "Unable to login " + user + " to " + securityDomain);
-    */
-    if (securityDomain.equals(metadata.getSecurityDomainURI())) {
-      // Propagate the login event to the security adapters
-      for (Iterator i = securityAdapterList.iterator(); i.hasNext();) {
-        ((SecurityAdapter) i.next()).login(user, password);
-      }
-    }
+  /**
+   * Define the contents of a model.
+   *
+   * @param uri the {@link URI} of the model to be redefined
+   * @param modelExpression the new content for the model
+   * @return RETURNED VALUE TO DO
+   * @throws QueryException if the model can't be modified
+   */
+  public synchronized long setModel(URI uri,
+      ModelExpression modelExpression) throws QueryException {
+    return this.setModel(null, uri, modelExpression);
   }
 
-  public Answer query(Query query) throws QueryException {
-    if (logger.isInfoEnabled()) {
-      logger.info("QUERY: " + query);
-    }
-
-    // Evaluate the query
-    QueryOperation queryOperation = new QueryOperation(query, this);
-    executeQuery(queryOperation);
-    return queryOperation.getAnswer();
-  }
-
-  static Answer doQuery(DatabaseSession databaseSession,
-                        SystemResolver  systemResolver,
-                        Query           query) throws Exception
-  {
-    Answer result;
-
-    LocalQuery localQuery =
-      new LocalQuery(query, systemResolver, databaseSession.operationContext);
-
-    transform(databaseSession, localQuery);
-
-    // Complete the numerical phase of resolution
-    Tuples tuples = localQuery.resolve();
-    result = new SubqueryAnswer(databaseSession.operationContext, systemResolver, tuples,
-        query.getVariableList());
-    tuples.close();
-    localQuery.close();
-
-    if (logger.isDebugEnabled()) {
-      logger.debug("Answer rows = " + result.getRowCount());
-    }
-    return result;
-  }
-
-
   /**
+   * Define the contents of a model via an inputstream
    *
-   * Perform in-place transformation of localQuery.
-   * Note: we really want to convert this to a functional form eventually.
+   * @param inputStream a remote inputstream
+   * @param destinationModelURI the {@link URI} of the model to be redefined
+   * @param modelExpression the new content for the model
+   * @return RETURNED VALUE TO DO
+   * @throws QueryException if the model can't be modified
    */
-  static void transform(DatabaseSession databaseSession, LocalQuery localQuery) throws Exception {
-    // Start with the symbolic phase of resolution
-    LocalQuery.MutableLocalQueryImpl mutableLocalQueryImpl =
-      localQuery.new MutableLocalQueryImpl();
-    if (symbolicLogger.isDebugEnabled()) {
-      symbolicLogger.debug("Before transformation: " + mutableLocalQueryImpl);
+  public synchronized long setModel(InputStream inputStream,
+      URI destinationModelURI,
+      ModelExpression modelExpression) throws QueryException {
+    if (logger.isInfoEnabled()) {
+      logger.info("SET-MODEL " + destinationModelURI + " to " + modelExpression +
+          " from " + inputStream);
     }
-    Iterator i = databaseSession.symbolicTransformationList.iterator();
-    while (i.hasNext()) {
-      SymbolicTransformation symbolicTransformation =
-        (SymbolicTransformation) i.next();
-      assert symbolicTransformation != null;
-      symbolicTransformation.transform(databaseSession.operationContext, mutableLocalQueryImpl);
-      if (mutableLocalQueryImpl.isModified()) {
-        // When a transformation succeeds, we rewind and start from the
-        // beginning of the symbolicTransformationList again
-        if (symbolicLogger.isDebugEnabled()) {
-          symbolicLogger.debug("Symbolic transformation: " +
-                               mutableLocalQueryImpl);
-        }
-        mutableLocalQueryImpl.close();
-        mutableLocalQueryImpl = localQuery.new MutableLocalQueryImpl();
-        i = databaseSession.symbolicTransformationList.iterator();
-      }
+    // Validate parameters
+    if (destinationModelURI == null) {
+      throw new IllegalArgumentException("Null 'destinationModelURI' parameter");
     }
-    mutableLocalQueryImpl.close();
-  }
-
-  void endPreviousQueryTransaction() throws QueryException {
-    logger.debug("Clearing previous transaction");
-
-    // Save the exception.
-    Throwable tmpThrowable = rollbackCause;
-
-    Set answers = new HashSet(outstandingAnswers);
-    Iterator i = answers.iterator();
-    while (i.hasNext()) {
-      try {
-        SubqueryAnswer s = (SubqueryAnswer) i.next();
-        operationContext.deregisterAnswer(s);
-        //Do not close tuples - for Jena and JRDF.
-        //s.close();
-      } catch (Throwable th) {
-        logger.debug("Failed to close preexisting answer", th);
-      }
+    if (modelExpression == null) {
+      throw new IllegalArgumentException("Null 'modelExpression' parameter");
     }
 
-    // If by closing the answer we have called endTransactionalBlock then
-    // throw the saved exception.
-    if ((rollbackCause == null) && (tmpThrowable != null) &&
-        (systemResolver == null)) {
-      throw new QueryException("Failure ending previous query", tmpThrowable);
+    // Convert the model expression into the source model URI
+    if (!(modelExpression instanceof ModelResource)) {
+      throw new QueryException("Unsupported model expression " +
+          modelExpression + " (" + modelExpression.getClass() + ")");
     }
+    assert modelExpression instanceof ModelResource;
 
-    try {
-      if (!outstandingAnswers.isEmpty()) {
-        throw new QueryException("Failed to clear preexisting transaction");
-      }
-      if (this.transaction != null) {
-        throw new QueryException("Failed to void suspended transaction");
-      }
-      if (transactionManager.getTransaction() != null) {
-        throw new QueryException("Failed to end transaction");
-      }
-    } catch (QueryException eq) {
-      endTransactionalBlock("Error ending previous query");
-      throw eq;
-    } catch (Throwable th) {
-      endTransactionalBlock("Error ending previous query");
-      throw new QueryException("Failure ending previous query", th);
-    }
-  }
+    URI sourceModelURI = ((ModelResource)modelExpression).getURI();
+    assert sourceModelURI != null;
 
-  public List query(List queryList) throws QueryException {
-
-    if (logger.isInfoEnabled()) {
-      StringBuffer log = new StringBuffer("QUERYING LIST: ");
-      for (int i = 0; i < queryList.size(); i++) {
-        log.append(queryList.get(i));
-      }
-      logger.info(log.toString());
+    // Perform the operation
+    SetModelOperation op = new SetModelOperation(sourceModelURI, destinationModelURI,
+                                  inputStream, contentHandlers, this);
+    // preExcecute is a rather ugly hack, get rid of it once we support re-entrant transactions.
+    if (op.preExecute()) {
+      execute(op, "Unable to load " + sourceModelURI + " into " + destinationModelURI);
     }
 
-    QueryOperation queryOperation = new QueryOperation(queryList, this);
-    executeQuery(queryOperation);
-    return queryOperation.getAnswerList();
+    return op.getStatementCount();
   }
 
-
   /**
    * {@inheritDoc}
    */
@@ -809,56 +678,52 @@
   }
 
 
-  public void removeModel(URI modelURI) throws QueryException {
+  public void setAutoCommit(boolean autoCommit) throws QueryException {
     if (logger.isInfoEnabled()) {
-      logger.info("REMOVE MODEL: " + modelURI);
+      logger.info("setAutoCommit(" + autoCommit + ") called with autoCommit = " + this.autoCommit);
     }
-    // Validate "modelURI" parameter
-    if (modelURI == null) {
-      throw new IllegalArgumentException("Null \"modelURI\" parameter");
-    }
 
-    execute(new RemoveModelOperation(modelURI), "Unable to remove " + modelURI);
+    if (!this.autoCommit && autoCommit) { // Turning autoCommit on
+      try {
+        resumeTransactionalBlock();
+      } finally {
+        this.autoCommit = true;
+        this.inFailedTransaction = false;
+        endTransactionalBlock("Extended transaction failed");
+      }
+    } else if (this.autoCommit && !autoCommit) { // Turning autoCommit off
+      if (this.transaction != null) {
+        resumeTransactionalBlock();
+        endPreviousQueryTransaction();
+      }
+      systemResolver = beginTransactionalBlock(true);
+      try {
+        suspendTransactionalBlock();
+      } catch (Throwable th) {
+        logger.error("Failed to suspend transaction", th);
+        rollbackTransactionalBlock(th);
+        endTransactionalBlock("Could set auto Commit off");
+      }
+      this.autoCommit = false;
+    } else if (this.inFailedTransaction) { // Reset after failed autoCommit off based transaction.
+      this.inFailedTransaction = false;
+    } else { // Leaving autoCommit the same
+      if (logger.isInfoEnabled()) {
+        logger.info("Invalid call to setAutoCommit(" + autoCommit + ") called with autoCommit = " + this.autoCommit);
+      }
+    }
   }
 
-  public boolean modelExists(URI modelURI) throws QueryException {
-    ModelExistsOperation operation = new ModelExistsOperation(modelURI);
-
-    execute(operation, "Failed to determine model existence");
-
-    return operation.getResult();
+  public void commit() throws QueryException {
+    logger.info("Committing transaction");
+    if (!autoCommit) {
+      synchronized (DatabaseSession.class) {
+        setAutoCommit(true);
+        setAutoCommit(false);
+      }
+    }
   }
 
-  /**
-   * Restore all the data on the specified server. If the database is not
-   * currently empty then the database will contain the union of its current
-   * content and the content of the backup file when this method returns.
-   *
-   * @param serverURI The URI of the server to restore.
-   * @param sourceURI The URI of the backup file to restore from.
-   * @throws QueryException if the restore cannot be completed.
-   */
-  public void restore(URI serverURI, URI sourceURI) throws QueryException {
-    this.restore(null, serverURI, sourceURI);
-  }
-
-  /**
-   * Restore all the data on the specified server. If the database is not
-   * currently empty then the database will contain the union of its current
-   * content and the content of the backup file when this method returns.
-   *
-   * @param inputStream a client supplied inputStream to obtain the restore
-   *        content from. If null assume the sourceURI has been supplied.
-   * @param serverURI The URI of the server to restore.
-   * @param sourceURI The URI of the backup file to restore from.
-   * @throws QueryException if the restore cannot be completed.
-   */
-  public void restore(InputStream inputStream, URI serverURI,
-      URI sourceURI) throws QueryException {
-    execute(new RestoreOperation(inputStream, serverURI, sourceURI),
-            "Unable to restore from " + sourceURI);
-  }
-
   public void rollback() throws QueryException {
     logger.info("Rollback transaction");
     if (autoCommit) {
@@ -885,102 +750,212 @@
     }
   }
 
-  public void setAutoCommit(boolean autoCommit) throws QueryException {
-    if (logger.isInfoEnabled()) {
-      logger.info("setAutoCommit(" + autoCommit + ") called with autoCommit = " + this.autoCommit);
-    }
+  public void close() throws QueryException {
+    logger.info("Closing session");
+    if (!autoCommit) {
+      logger.warn("Closing session while holding write-lock");
 
-    if (!this.autoCommit && autoCommit) { // Turning autoCommit on
       try {
         resumeTransactionalBlock();
+      } catch (Throwable th) {
+        releaseWriteLock();
+        throw new QueryException("Error while resuming transaction in close", th);
+      }
+
+      try {
+        rollbackTransactionalBlock(
+            new QueryException("Attempt to close session whilst in transaction"));
       } finally {
-        this.autoCommit = true;
-        this.inFailedTransaction = false;
-        endTransactionalBlock("Extended transaction failed");
+        endTransactionalBlock("Failed to release write-lock in close");
       }
-    } else if (this.autoCommit && !autoCommit) { // Turning autoCommit off
+    } else {
       if (this.transaction != null) {
         resumeTransactionalBlock();
         endPreviousQueryTransaction();
       }
-      systemResolver = beginTransactionalBlock(true);
-      try {
-        suspendTransactionalBlock();
-      } catch (Throwable th) {
-        logger.error("Failed to suspend transaction", th);
-        rollbackTransactionalBlock(th);
-        endTransactionalBlock("Could set auto Commit off");
+    }
+  }
+
+  public boolean isLocal() {
+    return true;
+  }
+
+  public void login(URI securityDomain, String user, char[] password) {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Login of " + user + " to " + securityDomain);
+    }
+
+    /*
+    execute(new LoginOperation(securityDomain, user, password),
+            "Unable to login " + user + " to " + securityDomain);
+    */
+    if (securityDomain.equals(metadata.getSecurityDomainURI())) {
+      // Propagate the login event to the security adapters
+      for (Iterator i = securityAdapterList.iterator(); i.hasNext();) {
+        ((SecurityAdapter) i.next()).login(user, password);
       }
-      this.autoCommit = false;
-    } else if (this.inFailedTransaction) { // Reset after failed autoCommit off based transaction.
-      this.inFailedTransaction = false;
-    } else { // Leaving autoCommit the same
-      if (logger.isInfoEnabled()) {
-        logger.info("Invalid call to setAutoCommit(" + autoCommit + ") called with autoCommit = " + this.autoCommit);
+    }
+  }
+
+  //
+  // Transaction control methods.  Implements LocalSession.
+  //
+
+  /**
+   * Start's or resumes a transaction for an operation.
+   *
+   * Using start/finish TransactionalOperation ensures properly matched pairs of
+   * begin/end and suspend/resume.
+   */
+  public void startTransactionalOperation(boolean needsWrite) throws
+      QueryException {
+    logger.info("Starting Transactional Operation");
+    if (opState != FINISH) {
+      throw new IllegalArgumentException(
+          "Attempt to start transactional operation during: " +
+          opStates[opState]);
+    }
+    if (autoCommit) {
+      if (this.transaction != null) {
+        resumeTransactionalBlock();
+        endPreviousQueryTransaction();
       }
+      beginTransactionalBlock(needsWrite);
+      logger.info("BEGIN new transaction.");
+      opState = BEGIN;
+    } else {
+      resumeTransactionalBlock();
+      logger.info("RESUME old transaction.");
+      opState = RESUME;
     }
   }
 
   /**
-   * Define the contents of a model.
+   * Mark the current transaction for rollback due to an exception.
    *
-   * @param uri the {@link URI} of the model to be redefined
-   * @param modelExpression the new content for the model
-   * @return RETURNED VALUE TO DO
-   * @throws QueryException if the model can't be modified
+   * This records the exception which caused the rollback in the
+   * {@link #rollbackCause} field.
    */
-  public synchronized long setModel(URI uri,
-      ModelExpression modelExpression) throws QueryException {
-    return this.setModel(null, uri, modelExpression);
+  public void rollbackTransactionalBlock(Throwable throwable) throws
+      QueryException {
+    logger.info("Rollback Transactional Block");
+    assert throwable != null;
+
+    try {
+      if (logger.isDebugEnabled()) {
+        logger.debug("Marking transaction for rollback", throwable);
+      }
+      transactionManager.setRollbackOnly();
+    } catch (Throwable e) {
+      logger.error("Needed to mark transaction for rollback", throwable);
+      logger.error("Unable to mark transaction for rollback", e);
+      throw new QueryException("Unable to mark transaction for rollback", e);
+    }
+
+    rollbackCause = throwable;
   }
 
   /**
-   * Define the contents of a model via an inputstream
+   * Ends's or suspends a transaction for an operation.
    *
-   * @param inputStream a remote inputstream
-   * @param destinationModelURI the {@link URI} of the model to be redefined
-   * @param modelExpression the new content for the model
-   * @return RETURNED VALUE TO DO
-   * @throws QueryException if the model can't be modified
+   * Using start/finish TransactionalOperation ensures properly matched pairs of
+   * begin/end and suspend/resume.
    */
-  public synchronized long setModel(InputStream inputStream,
-      URI destinationModelURI,
-      ModelExpression modelExpression) throws QueryException {
-    if (logger.isInfoEnabled()) {
-      logger.info("SET-MODEL " + destinationModelURI + " to " + modelExpression +
-          " from " + inputStream);
+  public void finishTransactionalOperation(String errorString) throws
+      QueryException {
+    logger.info("Finishing Transactional Operation");
+    if (logger.isDebugEnabled()) {
+      logger.debug("opState = " + opStates[opState]);
+      logger.debug("autoCommit = " + autoCommit);
     }
-    // Validate parameters
-    if (destinationModelURI == null) {
-      throw new IllegalArgumentException("Null 'destinationModelURI' parameter");
+    if (opState == FINISH) {
+      throw new IllegalArgumentException(
+          "Attempt to finish transactional operation during: " + opStates[opState]);
     }
-    if (modelExpression == null) {
-      throw new IllegalArgumentException("Null 'modelExpression' parameter");
+    if (autoCommit) {
+      try {
+        endTransactionalBlock(errorString);
+      } finally {
+        logger.info("FINISH(end) implicit transaction.");
+        opState = FINISH;
+      }
+    } else {
+      try {
+        suspendTransactionalBlock();
+      } catch (Throwable th) {
+        logger.error("Failed to suspend transaction", th);
+        try {
+          rollbackTransactionalBlock(new QueryException("Failed to suspend transaction"));
+        } finally {
+          endTransactionalBlock("Failed to suspend transaction at end of operation");
+        }
+      } finally {
+        logger.info("FINISH(suspend) explicit transaction.");
+        opState = FINISH;
+      }
     }
+  }
 
-    // Convert the model expression into the source model URI
-    if (!(modelExpression instanceof ModelResource)) {
-      throw new QueryException("Unsupported model expression " +
-          modelExpression + " (" + modelExpression.getClass() + ")");
+  /**
+   * Resumes the previously suspended transaction from the current session.
+   *
+   * @throws QueryException Must be called outside the try/catch(Throwable) block
+   * protecting the transaction.
+   */
+  public void resumeTransactionalBlock() throws QueryException {
+    logger.info("Resume Transactional Block");
+    if (transaction == null) {
+      throw new IllegalStateException("Attempt to resume unsuspended transaction");
+    } else if (inFailedTransaction == true) {
+      throw new IllegalStateException("Transaction already failed, set autocommit true to reset");
     }
-    assert modelExpression instanceof ModelResource;
 
-    URI sourceModelURI = ((ModelResource)modelExpression).getURI();
-    assert sourceModelURI != null;
+    try {
+      transactionManager.resume(this.transaction);
+      this.transaction = null;
+    } catch (Exception e) {
+      logger.error("Resume failed", e);
+      throw new QueryException("Failed to resume transaction", e);
+    }
+  }
 
-    // Perform the operation
-    SetModelOperation op = new SetModelOperation(sourceModelURI, destinationModelURI,
-                                  inputStream, contentHandlers, this);
-    // preExcecute is a rather ugly hack, get rid of it once we support re-entrant transactions.
-    if (op.preExecute()) {
-      execute(op, "Unable to load " + sourceModelURI + " into " + destinationModelURI);
+  /**
+   * Suspends current transaction, storing it in session for latter resumption.
+   *
+   * @throws Throwable Must be called inside the try/catch(Throwable) block
+   * protecting the transaction.
+   */
+  public void suspendTransactionalBlock() throws Throwable {
+    logger.info("Suspend Transactional Block");
+    if (transaction != null) {
+      throw new IllegalStateException(
+          "Attempt to suspend unresumed transaction.");
     }
+    if (logger.isInfoEnabled()) {
+      logger.info(
+         "Suspend Transactional Block autocommit=" + autoCommit +
+         " transaction status=" + StatusFormat.formatStatus(transactionManager)
+      );
+    }
 
-    return op.getStatementCount();
+    int status = transactionManager.getStatus();
+    if (!autoCommit &&
+        (status == Status.STATUS_MARKED_ROLLBACK ||
+         status == Status.STATUS_ROLLEDBACK ||
+         status == Status.STATUS_ROLLING_BACK)) {
+      inFailedTransaction = true;
+      throw new QueryException("Transaction marked for rollback");
+    }
+
+    this.transaction = transactionManager.suspend();
   }
 
+  public ResolverSession getResolverSession() {
+    return systemResolver;
+  }
+
   //
-  // Internal methods
+  // Internal Transactional Methods.  Not exposed via the interface.
   //
 
   /**
@@ -1025,107 +1000,6 @@
   }
 
   /**
-   * Execute an {@link Operation}.
-   *
-   * @param operation  the {@link Operation} to execute
-   * @param failureMessage  text to appear as the exception message if the
-   *   <var>operation</var> fails
-   * @throws QueryException if the <var>operation</var> fails
-   */
-  private void execute(Operation operation, String failureMessage)
-    throws QueryException
-  {
-    assert operation != null;
-
-    startTransactionalOperation(operation.isWriteOperation());
-
-    assert systemResolver != null;
-    try {
-      operation.execute(operationContext, systemResolver, resolverSessionFactory, metadata);
-    } catch (Throwable e) {
-      rollbackTransactionalBlock(e);
-    } finally {
-      finishTransactionalOperation(failureMessage);
-    }
-  }
-
-  /**
-   * Execute an {@link Operation}.
-   *
-   * @param operation  the {@link Operation} to execute
-   * @throws QueryException if the <var>operation</var> fails
-   */
-  private void executeQuery(Operation operation) throws QueryException
-  {
-    /*
-     * Transaction semantics:
-     * AC && Suspended  -> R clr E B . S
-     * AC && !Suspended -> B . S
-     * !AC              -> R clr . S
-     */
-    if (autoCommit) {
-      if (this.transaction != null) {
-        resumeTransactionalBlock();
-        endPreviousQueryTransaction();
-      }
-      beginTransactionalBlock(operation.isWriteOperation());
-    }
-    else {
-      resumeTransactionalBlock();
-    }
-
-    try {
-      operation.execute(operationContext,
-                        systemResolver,
-                        resolverSessionFactory,
-                        metadata);
-    }
-    catch (Throwable th) {
-      try {
-        logger.warn("Query failed", th);
-        rollbackTransactionalBlock(th);
-      } finally {
-        endPreviousQueryTransaction();
-        throw new QueryException("Failed to rollback failed transaction", th);
-      }
-    }
-
-    try {
-      suspendTransactionalBlock();
-    } catch (Throwable th) {
-      endPreviousQueryTransaction();
-      logger.error("Query should have thrown exception", th);
-      throw new IllegalStateException("Query should have thrown exception");
-    }
-  }
-
-  private void obtainWriteLock() throws InterruptedException {
-    logger.info("Trying to obtain write lock. ");
-    synchronized (DatabaseSession.class) {
-      if (DatabaseSession.writeSession == this) {
-        return;
-      }
-      while (DatabaseSession.writeSession != null) {
-        DatabaseSession.class.wait();
-      }
-      DatabaseSession.writeSession = this;
-      this.writing = true;
-      logger.info("Obtained write lock. ");
-    }
-  }
-
-  private void releaseWriteLock() {
-    synchronized (DatabaseSession.class) {
-      if (DatabaseSession.writeSession == this) {
-        logger.info("Releasing write lock");
-        DatabaseSession.writeSession = null;
-        this.writing = false;
-        DatabaseSession.class.notifyAll();
-      }
-    }
-  }
-
-  /**
    * Mark the end of a transactional block.
    *
    * This commits the current transaction if {@link #autoCommit} is
@@ -1176,6 +1050,81 @@
     }
   }
 
+  void endPreviousQueryTransaction() throws QueryException {
+    logger.debug("Clearing previous transaction");
+
+    // Save the exception.
+    Throwable tmpThrowable = rollbackCause;
+
+    Set answers = new HashSet(outstandingAnswers);
+    Iterator i = answers.iterator();
+    while (i.hasNext()) {
+      try {
+        SubqueryAnswer s = (SubqueryAnswer) i.next();
+        operationContext.deregisterAnswer(s);
+        //Do not close tuples - for Jena and JRDF.
+        //s.close();
+      } catch (Throwable th) {
+        logger.debug("Failed to close preexisting answer", th);
+      }
+    }
+
+    // If by closing the answer we have called endTransactionalBlock then
+    // throw the saved exception.
+    if ((rollbackCause == null) && (tmpThrowable != null) &&
+        (systemResolver == null)) {
+      throw new QueryException("Failure ending previous query", tmpThrowable);
+    }
+
+    try {
+      if (!outstandingAnswers.isEmpty()) {
+        throw new QueryException("Failed to clear preexisting transaction");
+      }
+      if (this.transaction != null) {
+        throw new QueryException("Failed to void suspended transaction");
+      }
+      if (transactionManager.getTransaction() != null) {
+        throw new QueryException("Failed to end transaction");
+      }
+    } catch (QueryException eq) {
+      endTransactionalBlock("Error ending previous query");
+      throw eq;
+    } catch (Throwable th) {
+      endTransactionalBlock("Error ending previous query");
+      throw new QueryException("Failure ending previous query", th);
+    }
+  }
+
+  private void obtainWriteLock() throws InterruptedException {
+    logger.info("Trying to obtain write lock. ");
+    synchronized (DatabaseSession.class) {
+      if (DatabaseSession.writeSession == this) {
+        return;
+      }
+      while (DatabaseSession.writeSession != null) {
+        DatabaseSession.class.wait();
+      }
+      DatabaseSession.writeSession = this;
+      this.writing = true;
+      logger.info("Obtained write lock. ");
+    }
+  }
+
+  private void releaseWriteLock() {
+    synchronized (DatabaseSession.class) {
+      if (DatabaseSession.writeSession == this) {
+        logger.info("Releasing write lock");
+        DatabaseSession.writeSession = null;
+        this.writing = false;
+        DatabaseSession.class.notifyAll();
+      }
+    }
+  }
+
+  //
+  // Internal support methods.
+  //
+
   /**
    * Clear the cache of temporary models.
    */
@@ -1297,183 +1246,151 @@
     }
   }
 
-  /**
-   * Mark the current transaction for rollback due to an exception.
-   *
-   * This records the exception which caused the rollback in the
-   * {@link #rollbackCause} field.
-   */
-  public void rollbackTransactionalBlock(Throwable throwable) throws
-      QueryException {
-    logger.info("Rollback Transactional Block");
-    assert throwable != null;
+  //
+  // Private accessors intended only for DatabaseOperationContext
+  //
 
-    try {
-      if (logger.isDebugEnabled()) {
-        logger.debug("Marking transaction for rollback", throwable);
-      }
-      transactionManager.setRollbackOnly();
-    } catch (Throwable e) {
-      logger.error("Needed to mark transaction for rollback", throwable);
-      logger.error("Unable to mark transaction for rollback", e);
-      throw new QueryException("Unable to mark transaction for rollback", e);
-    }
+  SystemResolver getSystemResolver() {
+    return systemResolver;
+  }
 
-    rollbackCause = throwable;
+  Transaction getTransaction() {
+    return transaction;
   }
 
+  boolean isWriting() {
+    return writing;
+  }
+
+  boolean ensureTransactionResumed() throws QueryException {
+    if (this.transaction != null) {
+      resumeTransactionalBlock();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+
   /**
-   * Suspends current transaction, storing it in session for latter resumption.
+   * Backup all the data on the specified server to a URI or an output stream.
+   * The database is not changed by this method.
    *
-   * @throws Throwable Must be called inside the try/catch(Throwable) block
-   * protecting the transaction.
+   * If an outputstream is supplied then the destinationURI is ignored.
+   *
+   * @param outputStream Optional output stream to receive the contents
+   * @param serverURI The URI of the server to backup.
+   * @param destinationURI Option URI of the file to backup into.
+   * @throws QueryException if the backup cannot be completed.
    */
-  public void suspendTransactionalBlock() throws Throwable {
-    logger.info("Suspend Transactional Block");
-    if (transaction != null) {
-      throw new IllegalStateException(
-          "Attempt to suspend unresumed transaction.");
-    }
+  private synchronized void backup(OutputStream outputStream,
+      URI serverURI,
+      URI destinationURI) throws QueryException {
+    execute(
+        new BackupOperation(outputStream, serverURI, destinationURI),
+        "Unable to backup to " + destinationURI
+        );
+  }
+
+  //
+  // Internal utility methods.
+  //
+  protected void modify(URI modelURI, Set statements, boolean insert) throws QueryException
+  {
     if (logger.isInfoEnabled()) {
-      logger.info(
-         "Suspend Transactional Block autocommit=" + autoCommit +
-         " transaction status=" + StatusFormat.formatStatus(transactionManager)
-      );
+      logger.info("Inserting statements into " + modelURI);
     }
+    if (logger.isDebugEnabled()) {
+      logger.debug("Inserting statements: " + statements);
+    }
 
-    int status = transactionManager.getStatus();
-    if (!autoCommit &&
-        (status == Status.STATUS_MARKED_ROLLBACK ||
-         status == Status.STATUS_ROLLEDBACK ||
-         status == Status.STATUS_ROLLING_BACK)) {
-      inFailedTransaction = true;
-      throw new QueryException("Transaction marked for rollback");
+    execute(new ModifyModelOperation(modelURI, statements, insert),
+            "Could not commit insert");
+  }
+
+  private void modify(URI modelURI, Query query, boolean insert) throws QueryException
+  {
+    if (logger.isInfoEnabled()) {
+      logger.info("INSERT QUERY: " + query + " into " + modelURI);
     }
 
-    this.transaction = transactionManager.suspend();
+    execute(new ModifyModelOperation(modelURI, query, insert, this),
+            "Unable to modify " + modelURI);
   }
 
   /**
-   * Resumes the previously suspended transaction from the current session.
+   * Execute an {@link Operation}.
    *
-   * @throws QueryException Must be called outside the try/catch(Throwable) block
-   * protecting the transaction.
+   * @param operation  the {@link Operation} to execute
+   * @param failureMessage  text to appear as the exception message if the
+   *   <var>operation</var> fails
+   * @throws QueryException if the <var>operation</var> fails
    */
-  public void resumeTransactionalBlock() throws QueryException {
-    logger.info("Resume Transactional Block");
-    if (transaction == null) {
-      throw new IllegalStateException("Attempt to resume unsuspended transaction");
-    } else if (inFailedTransaction == true) {
-      throw new IllegalStateException("Transaction already failed, set autocommit true to reset");
-    }
+  private void execute(Operation operation, String failureMessage)
+    throws QueryException
+  {
+    assert operation != null;
 
+    startTransactionalOperation(operation.isWriteOperation());
+
+    assert systemResolver != null;
     try {
-      transactionManager.resume(this.transaction);
-      this.transaction = null;
-    } catch (Exception e) {
-      logger.error("Resume failed", e);
-      throw new QueryException("Failed to resume transaction", e);
+      operation.execute(operationContext, systemResolver, resolverSessionFactory, metadata);
+    } catch (Throwable e) {
+      rollbackTransactionalBlock(e);
+    } finally {
+      finishTransactionalOperation(failureMessage);
     }
   }
 
   /**
-   * Start's or resumes a transaction for an operation.
+   * Execute an {@link Operation}.
    *
-   * Using start/finish TransactionalOperation ensures properly matched pairs of
-   * begin/end and suspend/resume.
+   * @param operation  the {@link Operation} to execute
+   * @throws QueryException if the <var>operation</var> fails
    */
-  public void startTransactionalOperation(boolean needsWrite) throws
-      QueryException {
-    logger.info("Starting Transactional Operation");
-    if (opState != FINISH) {
-      throw new IllegalArgumentException(
-          "Attempt to start transactional operation during: " +
-          opStates[opState]);
-    }
+  private void executeQuery(Operation operation) throws QueryException
+  {
+    /*
+     * Transaction semantics:
+     * AC && Suspended  -> R clr E B . S
+     * AC && !Suspended -> B . S
+     * !AC              -> R clr . S
+     */
     if (autoCommit) {
       if (this.transaction != null) {
         resumeTransactionalBlock();
         endPreviousQueryTransaction();
       }
-      beginTransactionalBlock(needsWrite);
-      logger.info("BEGIN new transaction.");
-      opState = BEGIN;
-    } else {
+      beginTransactionalBlock(operation.isWriteOperation());
+    }
+    else {
       resumeTransactionalBlock();
-      logger.info("RESUME old transaction.");
-      opState = RESUME;
     }
-  }
 
-  /**
-   * Ends's or suspends a transaction for an operation.
-   *
-   * Using start/finish TransactionalOperation ensures properly matched pairs of
-   * begin/end and suspend/resume.
-   */
-  public void finishTransactionalOperation(String errorString) throws
-      QueryException {
-    logger.info("Finishing Transactional Operation");
-    if (logger.isDebugEnabled()) {
-      logger.debug("opState = " + opStates[opState]);
-      logger.debug("autoCommit = " + autoCommit);
+    try {
+      operation.execute(operationContext,
+                        systemResolver,
+                        resolverSessionFactory,
+                        metadata);
     }
-    if (opState == FINISH) {
-      throw new IllegalArgumentException(
-          "Attempt to finish transactional operation during: " + opStates[opState]);
-    }
-    if (autoCommit) {
+    catch (Throwable th) {
       try {
-        endTransactionalBlock(errorString);
+        logger.warn("Query failed", th);
+        rollbackTransactionalBlock(th);
       } finally {
-        logger.info("FINISH(end) implicit transaction.");
-        opState = FINISH;
+        endPreviousQueryTransaction();
+        throw new QueryException("Failed to rollback failed transaction", th);
       }
-    } else {
-      try {
-        suspendTransactionalBlock();
-      } catch (Throwable th) {
-        logger.error("Failed to suspend transaction", th);
-        try {
-          rollbackTransactionalBlock(new QueryException("Failed to suspend transaction"));
-        } finally {
-          endTransactionalBlock("Failed to suspend transaction at end of operation");
-        }
-      } finally {
-        logger.info("FINISH(suspend) explicit transaction.");
-        opState = FINISH;
-      }
     }
-  }
 
-  public boolean isLocal() {
-    return true;
-  }
-
-  //
-  // Private accessors intended only for DatabaseOperationContext
-  //
-
-  SystemResolver getSystemResolver() {
-    return systemResolver;
-  }
-
-  Transaction getTransaction() {
-    return transaction;
-  }
-
-  boolean isWriting() {
-    return writing;
-  }
-
-  boolean ensureTransactionResumed() throws QueryException {
-    if (this.transaction != null) {
-      resumeTransactionalBlock();
-      return true;
-    } else {
-      return false;
+    try {
+      suspendTransactionalBlock();
+    } catch (Throwable th) {
+      endPreviousQueryTransaction();
+      logger.error("Query should have thrown exception", th);
+      throw new IllegalStateException("Query should have thrown exception");
     }
   }
 
-
 }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/LocalQuery.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/LocalQuery.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/LocalQuery.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -246,7 +246,6 @@
 
       if (logger.isDebugEnabled()) {
         logger.debug("Tuples result = " + TuplesOperations.formatTuplesTree(result));
-        logger.debug("Raw result " + result);
       }
 
       result = projectSelectClause(result);

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/ModifyModelOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/ModifyModelOperation.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/ModifyModelOperation.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -199,8 +199,7 @@
     else {
       assert query != null;
 
-      Answer answer =
-        DatabaseSession.doQuery(databaseSession, systemResolver, query);
+      Answer answer = operationContext.doQuery(systemResolver, query);
       Variable[] vars = answer.getVariables();
       assert vars.length == 3;
       statements = new TuplesWrapperStatements(

Copied: trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java (from rev 105, branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java)
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -0,0 +1,257 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+package org.mulgara.resolver;
+
+// Java 2 enterprise packages
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+
+// Third party packages
+import org.apache.log4j.Logger;
+
+// Local packages
+import org.mulgara.resolver.spi.DatabaseMetadata;
+import org.mulgara.resolver.spi.Resolver;
+import org.mulgara.resolver.spi.ResolverSessionFactory;
+
+import org.mulgara.query.TuplesException;
+
+/**
+ * Responsible for the javax.transaction.Transaction object.
+ * Responsibilities
+ * Ensuring every begin or resume is followed by either a suspend or an end.
+ * Ensuring every suspend or end is preceeded by either a begin or a resume.
+ * In conjunction with TransactionalAnswer ensuring that
+ * all calls to operations on SubqueryAnswer are preceeded by a successful resume.
+ * all calls to operations on SubqueryAnswer conclude with a suspend as the last call prior to returning to the user.
+ * Collaborates with DatabaseTransactionManager to determine when to end the transaction.
+ *
+ * @created 2006-10-06
+ *
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ *
+ * @version $Revision: $
+ *
+ * @modified $Date: $
+ *
+ * @maintenanceAuthor $Author: $
+ *
+ * @company <A href="mailto:mail at netymon.com">Netymon Pty Ltd</A>
+ *
+ * @copyright &copy;2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+public class MulgaraTransaction {
+  /** Logger.  */
+  private static final Logger logger =
+    Logger.getLogger(MulgaraTransaction.class.getName());
+
+  private MulgaraTransactionManager manager;
+  private OperationContext context;
+
+  private Transaction transaction;
+  private Thread currentThread;
+
+  private int inuse;
+  private int using;
+
+  private final int NO_ROLLBACK = 0;
+  private final int IMPLICIT_ROLLBACK = 1;
+  private final int EXPLICIT_ROLLBACK = 2;
+
+  private int rollback;
+  private Throwable rollbackCause;
+
+  public MulgaraTransaction(MulgaraTransactionManager manager, OperationContext context) {
+    this.manager = manager;
+    this.context = context;
+
+//    FIXME: MTMgr will be null until operational.
+//    this.transaction = manager.transactionStart(this);
+    inuse = 1; // Note: This implies implict activation as a part of construction.
+    using = 0;
+
+    rollback = NO_ROLLBACK;
+    rollbackCause = null;
+
+//    FIXME: need this added to context. Sets up and enlists the system-resolver.
+//    context.initiate();
+
+//    FIXME: need this added to context. Allows context to cleanup caches at end of transaction.
+//    this.transaction.enlistResource(context.getXAResource());
+  }
+
+  // FIXME: Not yet certain I have the error handling right here.
+  // Need to clarify semantics and ensure the error conditions are 
+  // properly handled.
+  private synchronized void activate() throws MulgaraTransactionException {
+    if (currentThread == null) {
+      currentThread = Thread.currentThread();
+    } else if (!currentThread.equals(Thread.currentThread())) {
+      throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
+    }
+
+    if (inuse == 0) {
+      try {
+        manager.transactionResumed(this);
+      } catch (Throwable th) {
+        logger.warn("Error resuming transaction: ", th);
+        failTransaction();
+        throw new MulgaraTransactionException("Error resuming transaction", th);
+      }
+    }
+
+    inuse++;
+  }
+
+
+  // FIXME: Not yet certain I have the error handling right here.
+  // Need to clarify semantics and ensure the error conditions are 
+  // properly handled.
+  private synchronized void deactivate() throws MulgaraTransactionException {
+
+    inuse--;
+
+    if (inuse < 0) {
+      throw implicitRollback(
+          new MulgaraTransactionException("Mismatched activate/deactivate.  inuse < 0: " + inuse));
+    }
+
+    if (inuse == 0) {
+      if (using == 0) {
+        // END TRANSACTION HERE.  But commit might fail.
+        manager.transactionComplete(this);
+      } else {
+        // What happens if suspend fails?
+        // Rollback and terminate transaction.
+        // JTA isn't entirely unambiguous as to the long-term stability of the original
+        // transaction object - can suspend return a new object?
+        this.transaction = manager.transactionSuspended(this);
+      }
+      currentThread = null;
+    }
+  }
+
+  // Do I want to check for currentThread here?  Do I want a seperate check() method to 
+  // cover precondition checks against currentThread?
+  void reference() {
+    using++;
+  }
+
+  void dereference() throws MulgaraTransactionException {
+    using--;
+    if (using < 0) {
+      throw implicitRollback(new MulgaraTransactionException("ERROR: Transaction dereferenced more times than referenced!"));
+    }
+  }
+
+  void execute(Operation operation,
+               ResolverSessionFactory resolverSessionFactory, // FIXME: We shouldn't need this. - only used for backup and restore operations.
+               DatabaseMetadata metadata) throws MulgaraTransactionException {
+    activate();
+    try {
+//      FIXME: Need to migrate systemResolver to context for this to work.
+//      operation.execute(context,
+//                        context.getSystemResolver(),
+//                        resolverSessionFactory,
+//                        metadata);
+    } catch (Throwable th) {
+      throw implicitRollback(th);
+    } finally {
+      deactivate();
+    }
+  }
+
+  /** Should rename this 'wrap' */
+  AnswerOperationResult execute(AnswerOperation ao) throws TuplesException {
+//    FIXME: activate/deactivate won't work until we have MTMgr operational.
+//    activate();
+    try {
+      ao.execute();
+      return ao.getResult();
+    } catch (Throwable th) {
+      throw new TuplesException("Error accessing Answer", th);
+//      throw implicitRollback(th);
+    } finally {
+//      deactivate();
+    }
+  }
+
+
+  private MulgaraTransactionException implicitRollback(Throwable cause) throws MulgaraTransactionException {
+    rollback = IMPLICIT_ROLLBACK;
+    rollbackCause = cause;
+    failTransaction();
+    return new MulgaraTransactionException("Transaction Rolledback", cause);
+  }
+
+  /**
+   * Note: I think this is the only one that matters. 
+   */
+  protected void explicitRollback() throws MulgaraTransactionException {
+    rollback = EXPLICIT_ROLLBACK;
+    // We don't throw an exception here when transaction fails - this is expected,
+    // after all we requested it.
+  }
+
+  private void terminateTransaction() throws MulgaraTransactionException {
+  }
+
+  private void failTransaction() throws MulgaraTransactionException {
+    // We need to handle the whole fact this is an error, but the core operation is rollback.
+    try {
+      transaction.rollback();
+    } catch (SystemException es) {
+      throw new MulgaraTransactionException("Failed to Rollback", es);
+    }
+  }
+
+  private void finalizeTransaction() throws MulgaraTransactionException {
+    // We need a whole load of error handling here, but the core operation is commit.
+    try {
+      transaction.commit();
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Error while trying to commit", e);
+    } finally {
+      manager.transactionComplete(this);
+    }
+  }
+
+  //
+  // Note that OperationContext needs to be decoupled from DatabaseSession, so that it is
+  // recreated for each operation - it also needs to provide an XAResource of it's own for
+  // enlisting in the transaction to clean-up caches and the like.
+  //
+  public void enlistResolver(Resolver resolver) throws MulgaraTransactionException {
+    try {
+      XAResource resource = resolver.getXAResource();
+      transaction.enlistResource(resource);
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Error enlisting resolver", e);
+    }
+  }
+
+  /**
+   * Should only be visible to MulgaraTransactionManager.
+   */
+  protected Transaction getTransaction() {
+    return transaction;
+  }
+}

Added: trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionException.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionException.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionException.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -0,0 +1,47 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+package org.mulgara.resolver;
+
+/**
+ * Exception to indicate a failure within the transaction handling.
+ *
+ * @created 2006-10-06
+ *
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ *
+ * @version $Revision: $
+ *
+ * @modified $Date: $
+ *
+ * @maintenanceAuthor $Author: $
+ *
+ * @company <A href="mailto:mail at netymon.com">Netymon Pty Ltd</A>
+ *
+ * @copyright &copy;2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+public class MulgaraTransactionException extends Exception
+{
+  public MulgaraTransactionException(String message) {
+    super(message);
+  }
+
+  public MulgaraTransactionException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}

Copied: trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java (from rev 105, branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java)
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -0,0 +1,282 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+
+package org.mulgara.resolver;
+
+// Java2 packages
+import java.util.HashMap;
+import java.util.Map;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+
+// Third party packages
+import org.apache.log4j.Logger;
+
+// Local packages
+import org.mulgara.server.Session;
+
+/**
+ * Manages transactions within Mulgara.
+ *
+ * see http://mulgara.org/confluence/display/dev/Transaction+Architecture
+ *
+ * Maintains association between Answer's and TransactionContext's.
+ * Manages tracking the ownership of the write-lock.
+ * Maintains the write-queue and any timeout algorithm desired.
+ * Provides new/existing TransactionContext's to DatabaseSession on request.
+ *    Note: Returns new context unless Session is currently in a User Demarcated Transaction.
+ * 
+ *
+ * @created 2006-10-06
+ *
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ *
+ * @version $Revision: $
+ *
+ * @modified $Date: $
+ *
+ * @maintenanceAuthor $Author: $
+ *
+ * @company <A href="mailto:mail at netymon.com">Netymon Pty Ltd</A>
+ *
+ * @copyright &copy;2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+
+public class MulgaraTransactionManager {
+  /** Logger.  */
+  private static final Logger logger =
+    Logger.getLogger(MulgaraTransactionManager.class.getName());
+
+  // Write lock is associated with a session.
+  private Session currentWritingSession;
+  private MulgaraTransaction userTransaction;
+
+  /** Map from session to transaction for all 'write' transactions that have been rolledback. */
+  private Map failedSessions;
+
+  /** Map from thread to associated transaction. */
+  private Map activeTransactions;
+
+  private TransactionManager transactionManager;
+
+  private Object writeLockMutex;
+
+  public MulgaraTransactionManager(TransactionManager transactionManager) {
+    this.currentWritingSession = null;
+    this.userTransaction = null;
+
+    this.failedSessions = new HashMap();
+
+    this.transactionManager = transactionManager;
+    this.writeLockMutex = new Object();
+  }
+
+  /**
+   * Allows DatabaseSession to initiate/obtain a transaction.
+   * <ul>
+   * <li>If the Session holds the write lock, return the current Write-Transaction.</li>
+   * <li>If the Session does not hold the write lock and requests a read-only transaction,
+   *     create a new ro-transaction object and return it.</li>
+   * <li>If the Session does not hold the write lock and requests a read-write transaction,
+   *     obtain the write-lock, create a new transaction object and return it.</li>
+   * </ul>
+   */
+  public synchronized MulgaraTransaction getTransaction(DatabaseSession session, boolean write) throws MulgaraTransactionException {
+    if (session == currentWritingSession) {
+      return userTransaction;
+    } 
+
+    if (write) {
+      obtainWriteLock(session);
+    }
+
+//    FIXME: Need to finish 1-N DS-OC and provide this method - should really be newOperationContext.
+//    return new MulgaraTransaction(this, session.getOperationContext());
+    return new MulgaraTransaction(this, null);
+  }
+
+
+  public synchronized MulgaraTransaction getTransaction()
+      throws MulgaraTransactionException {
+    MulgaraTransaction transaction = (MulgaraTransaction)activeTransactions.get(Thread.currentThread());
+    if (transaction != null) {
+      return transaction;
+    } else {
+      throw new MulgaraTransactionException("No transaction assoicated with current thread");
+    }
+  }
+
+  private synchronized void obtainWriteLock(Session session)
+      throws MulgaraTransactionException {
+    while (currentWritingSession != null) {
+      try {
+        writeLockMutex.wait();
+      } catch (InterruptedException ei) {
+        throw new MulgaraTransactionException("Interrupted while waiting for write lock", ei);
+      }
+    }
+    currentWritingSession = session;
+  }
+
+  private synchronized void releaseWriteLock() {
+    currentWritingSession = null;
+    userTransaction = null;
+    writeLockMutex.notify();
+  }
+
+
+  public synchronized void commit(Session session) throws MulgaraTransactionException {
+    setAutoCommit(session, true);
+    setAutoCommit(session, false);
+  }
+
+
+  /**
+   * This is an explicit, user-specified rollback.
+   * This 
+   * This needs to be distinguished from an implicit rollback triggered by failure.
+   */
+  public synchronized void rollback(Session session) throws MulgaraTransactionException {
+    if (session == currentWritingSession) {
+      try {
+        userTransaction.explicitRollback();
+        finalizeTransaction();
+      } finally {
+        failedSessions.put(currentWritingSession, userTransaction);
+        userTransaction = null;
+        currentWritingSession = null;
+        releaseWriteLock();
+      }
+    } else {
+      // We have a problem - rollback called on session that doesn't have a transaction active.
+    }
+  }
+
+  public synchronized void setAutoCommit(Session session, boolean autoCommit)
+      throws MulgaraTransactionException {
+    if (session == currentWritingSession && failedSessions.containsKey(session)) {
+      // CRITICAL ERROR - transaction failed, but we did not finalise it.
+    }
+
+    if (session == currentWritingSession || failedSessions.containsKey(session)) {
+      if (autoCommit) {
+        // AutoCommit off -> on === branch on current state of transaction.
+        if (session == currentWritingSession) {
+          // Within active transaction - commit and finalise.
+          try {
+          } finally {
+            releaseWriteLock();
+          }
+        } else {
+          // Within failed transaction - cleanup and finalise.
+          failedSessions.remove(session);
+        }
+      } else {
+        // AutoCommit off -> off === no-op. Log info.
+      }
+    } else {
+      if (autoCommit) {
+        // AutoCommit on -> on === no-op.  Log info.
+      } else {
+        // AutoCommit on -> off == Start new transaction.
+        obtainWriteLock(session);
+//        FIXME: finish DS-OC first.
+//        userTransaction = new MulgaraTransaction(this, session.newOperationContext(true));
+        currentWritingSession = session;
+      }
+    }
+  }
+
+  public synchronized void closingSession(Session session) throws MulgaraTransactionException {
+    // Check if we hold the write lock, if we do then rollback and throw exception.
+    // Otherwise - no-op.
+  }
+
+  public void finalizeTransaction() {
+    throw new IllegalStateException("mmmm I was doing something here. I need to remember what.");
+  }
+
+  //
+  // Transaction livecycle callbacks.
+  //
+
+  public synchronized Transaction transactionStart(MulgaraTransaction transaction)
+      throws MulgaraTransactionException {
+    try {
+      transactionManager.begin();
+      Transaction jtaTrans = transactionManager.getTransaction();
+
+      activeTransactions.put(Thread.currentThread(), transaction);
+
+      return jtaTrans;
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Transaction Begin Failed", e);
+    }
+  }
+
+  public synchronized void transactionResumed(MulgaraTransaction transaction) 
+      throws MulgaraTransactionException {
+    if (activeTransactions.get(Thread.currentThread()) != null) {
+      throw new MulgaraTransactionException(
+          "Attempt to resume transaction in already activated thread");
+    } else if (activeTransactions.containsValue(transaction)) {
+      throw new MulgaraTransactionException(
+          "Attempt to resume resumed transaction");
+    }
+    
+    try {
+      transactionManager.resume(transaction.getTransaction());
+      activeTransactions.put(Thread.currentThread(), transaction);
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Resume Failed", e);
+    }
+  }
+
+  public synchronized Transaction transactionSuspended(MulgaraTransaction transaction)
+      throws MulgaraTransactionException {
+    try {
+      if (transaction != activeTransactions.get(Thread.currentThread())) {
+        throw new MulgaraTransactionException(
+            "Attempt to commit transaction from outside thread");
+      }
+
+      return transactionManager.suspend();
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Suspend failed", e);
+    } finally {
+      activeTransactions.remove(Thread.currentThread());
+    }
+
+  }
+
+  public synchronized void transactionComplete(MulgaraTransaction transaction) 
+      throws MulgaraTransactionException {
+    try {
+      transactionManager.commit();
+      if (transaction == userTransaction) {
+        releaseWriteLock();
+      }
+    } catch (Exception e) {
+      throw new MulgaraTransactionException("Commit Failed", e);
+    } finally {
+      activeTransactions.remove(Thread.currentThread());
+    }
+  }
+}

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/OperationContext.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/OperationContext.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/OperationContext.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -32,6 +32,8 @@
 import java.util.List;
 
 // Local packages
+import org.mulgara.query.Answer;
+import org.mulgara.query.Query;
 import org.mulgara.query.QueryException;
 import org.mulgara.resolver.spi.GlobalizeException;
 import org.mulgara.resolver.spi.Resolver;
@@ -114,4 +116,10 @@
    */
   public long getCanonicalModel(long model);
 
+  /**
+   * Here for the moment while we fix transactions.
+   * Oct 2006 - if it's still here after Dec 2006 let someone know it's been
+   * forgotten.
+   */
+  public Answer doQuery(SystemResolver systemResolver, Query query) throws Exception;
 }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/QueryOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/QueryOperation.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/QueryOperation.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -153,16 +153,14 @@
     if (query != null) {
       assert queryList == null;
 
-      answer = DatabaseSession.doQuery(databaseSession, systemResolver, query);
+      answer = operationContext.doQuery(systemResolver, query);
     }
     else {
       assert queryList != null;
 
       answerList = new ArrayList(queryList.size());
       for (Iterator i = queryList.iterator(); i.hasNext();) {
-        answerList.add(DatabaseSession.doQuery(databaseSession,
-                                               systemResolver,
-                                               (Query) i.next()));
+        answerList.add(operationContext.doQuery(systemResolver, (Query) i.next()));
       }
     }
   }

Copied: trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java (from rev 98, branches/xafix/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java)
===================================================================
--- branches/xafix/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java	2006-10-10 10:50:08 UTC (rev 98)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -0,0 +1,179 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+
+package org.mulgara.resolver;
+
+import org.mulgara.query.Answer;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.Variable;
+
+/**
+ * A transactional answer.  
+ * Wraps all calls to the enclosed answer object, ensuring all calls are made
+ * within an activated transactional context.  Also ensures that that context
+ * is deactivated upon returning from the outer-call.
+ *
+ * @created 2006-10-06
+ *
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ *
+ * @version $Revision: $
+ *
+ * @modified $Date: $
+ *
+ * @maintenanceAuthor $Author: $
+ *
+ * @company <A href="mailto:mail at netymon.com">Netymon Pty Ltd</A>
+ *
+ * @copyright &copy;2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+
+public class TransactionalAnswer implements Answer {
+
+  private Answer answer;
+
+  private MulgaraTransaction transaction;
+
+  public TransactionalAnswer(MulgaraTransaction transaction, Answer answer) {
+    this.answer = answer;
+    this.transaction = transaction;
+    transaction.reference();
+  }
+
+  public Object getObject(final int column) throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnObject(answer.getObject(column));
+        }
+      }).getObject();
+  }
+
+  public Object getObject(final String columnName) throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnObject(answer.getObject(columnName));
+        }
+      });
+  }
+
+  public void beforeFirst() throws TuplesException {
+    transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          answer.beforeFirst();
+        }
+      });
+  }
+
+  public void close() throws TuplesException {
+    transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          answer.close();
+          try {
+            transaction.dereference();
+          } catch (MulgaraTransactionException em) {
+            throw new TuplesException("Error dereferencing transaction", em);
+          }
+        }
+      });
+  }
+
+  public int getColumnIndex(final Variable column) throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnInt(answer.getColumnIndex(column));
+        }
+      }).getInt();
+  }
+
+  public int getNumberOfVariables() {
+    try {
+      return transaction.execute(new AnswerOperation() {
+          public void execute() {
+            returnInt(answer.getNumberOfVariables());
+          }
+        }).getInt();
+    } catch (TuplesException et) {
+      throw new IllegalStateException("Doesn't throw TuplesException", et);
+    }
+  }
+
+  public Variable[] getVariables() {
+    try {
+      return (Variable[])(transaction.execute(new AnswerOperation() {
+          public void execute() {
+            returnObject(answer.getVariables());
+          }
+        }).getObject());
+    } catch (TuplesException et) {
+      throw new IllegalStateException("Doesn't throw TuplesException", et);
+    }
+  }
+
+  public boolean isUnconstrained() throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnBoolean(answer.isUnconstrained());
+        }
+      }).getBoolean();
+  }
+
+  public long getRowCount() throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnLong(answer.getRowCount());
+        }
+      }).getLong();
+  }
+
+  public long getRowUpperBound() throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnLong(answer.getRowUpperBound());
+        }
+      }).getLong();
+  }
+
+  public int getRowCardinality() throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnInt(answer.getRowCardinality());
+        }
+      }).getInt();
+  }
+
+  public boolean next() throws TuplesException {
+    return transaction.execute(new AnswerOperation() {
+        public void execute() throws TuplesException {
+          returnBoolean(answer.next());
+        }
+      }).getBoolean();
+  }
+
+  public Object clone() {
+    try {
+      TransactionalAnswer c = (TransactionalAnswer)super.clone();
+      c.answer = (Answer)this.answer.clone();
+      c.transaction.reference();
+
+      return c;
+    } catch (CloneNotSupportedException ec) {
+      throw new IllegalStateException("Clone failed on Cloneable");
+    }
+  }
+}

Modified: trunk/src/jar/resolver-spi/java/org/mulgara/resolver/spi/LocalSession.java
===================================================================
--- trunk/src/jar/resolver-spi/java/org/mulgara/resolver/spi/LocalSession.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver-spi/java/org/mulgara/resolver/spi/LocalSession.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -38,6 +38,8 @@
  *       extension API.  Expect it to change in signifigant, non-backwards compatible
  *       ways.
  *
+ * Note: Deprecation warnings removed Oct 2006 to facilitate complete removal of
+ *       this interface.  THIS ENTIRE INTERFACE IS DEPRECATED DO NOT USE.
  * @created 2004-11-10
  *
  * @author Andrew Newman
@@ -61,8 +63,6 @@
    * Using start/finish TransactionalOperation ensures properly matched pairs of
    * begin/end and suspend/resume.
    *
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public void startTransactionalOperation(boolean needsWrite)
       throws QueryException;
@@ -71,8 +71,6 @@
    * Mark the current transaction for rollback due to an exception.
    *
    * @param throwable  the exception which caused the rollback
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public void rollbackTransactionalBlock(Throwable throwable)
       throws QueryException;
@@ -83,8 +81,6 @@
    * Using start/finish TransactionalOperation ensures properly matched pairs of
    * begin/end and suspend/resume.
    *
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public void finishTransactionalOperation(String errorString)
       throws QueryException;
@@ -94,8 +90,6 @@
    *
    * @throws QueryException Must be called outside the try/catch(Throwable) block
    * protecting the transaction.
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public void resumeTransactionalBlock() throws QueryException;
 
@@ -104,8 +98,6 @@
    *
    * @throws Throwable Must be called inside the try/catch(Throwable) block
    * protecting the transaction.
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public void suspendTransactionalBlock() throws Throwable;
 
@@ -113,8 +105,6 @@
    * Returns the current Resolver Session.
    *
    * @return the current resolver session.
-   * @deprecated This should not be relied upon to create resolvers as this
-   *     should go away.
    */
   public ResolverSession getResolverSession();
 }

Modified: trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/SessionView.java
===================================================================
--- trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/SessionView.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/SessionView.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -37,8 +37,14 @@
  * the ResolverFactory query rewriting interface.
  *
  * Use of this interface is HIGHLY discouraged!  
- * @deprecated If you require this functionality, let the core team know as it
- *             may affect scheduling of the new query rewriting interface.
+ *
+ * If you require this functionality, let the core team know as it
+ * may affect scheduling of the new query rewriting interface.
+ *
+ * Deprecation warnings removed to assist with development in this area.
+ * Note: This interface is considered deprecated.  If you don't tell us you're
+ * using it it might just disappear without warning.
+ *
  * @created 2004-09-22
  * @author <a href="http://staff.tucanatech.com/andrae">Andrae Muys</a>
  * @version $Revision: 1.8 $

Modified: trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/ViewMarker.java
===================================================================
--- trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/ViewMarker.java	2006-10-25 22:35:58 UTC (rev 112)
+++ trunk/src/jar/resolver-spi/java/org/mulgara/resolver/view/ViewMarker.java	2006-10-26 13:56:44 UTC (rev 113)
@@ -37,8 +37,14 @@
  * the ResolverFactory query rewriting interface.
  *
  * Use of this interface is HIGHLY discouraged!  
- * @deprecated If you require this functionality, let the core team know as it
- *             may affect scheduling of the new query rewriting interface.
+ *
+ * If you require this functionality, let the core team know as it
+ * may affect scheduling of the new query rewriting interface.
+ *
+ * Deprecation warnings removed to assist with development in this area.
+ * Note: This interface is considered deprecated.  If you don't tell us you're
+ * using it it might just disappear without warning.
+ *
  * @created 2004-09-22
  * @author <a href="http://staff.tucanatech.com/andrae">Andrae Muys</a>
  * @version $Revision: 1.8 $




More information about the Mulgara-svn mailing list