[Mulgara-svn] r520 - branches/nw-interface/src/jar/itql/java/org/mulgara/itql

pag at mulgara.org pag at mulgara.org
Tue Nov 6 21:45:44 UTC 2007


Author: pag
Date: 2007-11-06 15:45:44 -0600 (Tue, 06 Nov 2007)
New Revision: 520

Modified:
   branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java
Log:
Added a lot of state management to handle entering and leaving transactions correctly, and committing and rolling back while there.

Modified: branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java
===================================================================
--- branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java	2007-11-06 21:42:26 UTC (rev 519)
+++ branches/nw-interface/src/jar/itql/java/org/mulgara/itql/TqlAutoInterpreter.java	2007-11-06 21:45:44 UTC (rev 520)
@@ -109,7 +109,7 @@
       // set up a connection, if required
       Connection conn = establishConnection(cmd);
       handleResult(cmd.execute(conn), cmd);
-      updateOnConnectionState(conn);
+      updateConnectionsForTx(conn, cmd);
       lastMessage = cmd.getResultMessage();
     } catch (Exception e) {
       lastException = e;
@@ -143,12 +143,14 @@
 
   /**
    * Perform any actions required on the update of the state of a connection.
+   * Most commands will skip through this method. Only transaction commands do anything.
    * @param conn The connection whose state needs checking.
    * @throws QueryException Can be caused by a failed change into a transaction.
    */
-  void updateOnConnectionState(Connection conn) throws QueryException {
-    // check if the transaction state changed.
+  void updateConnectionsForTx(Connection conn, Command cmd) throws QueryException {
+    // check if the transaction state changed. This will happen for setAutocommit operations.
     if (inTransaction == conn.getAutoCommit()) {
+      assert cmd instanceof org.mulgara.query.operation.SetAutoCommit;
       // update the client state
       inTransaction = !conn.getAutoCommit();
       if (inTransaction) {
@@ -159,6 +161,9 @@
         for (Connection c: transConnections) c.setAutoCommit(true);
         transConnections.clear();
       }
+    } else if (cmd.isTxOperation()) {
+      // this is a commit or rollback.  Inform every connection we've used in this transaction.
+      operateOnTxConnections(cmd);
     }
   }
 
@@ -177,6 +182,7 @@
    * Close any resources that are still in use.
    */
   public void close() {
+    transConnections.clear();
     connectionFactory.closeAll();
   }
 
@@ -214,35 +220,48 @@
   private Connection establishConnection(Command cmd) throws ConnectionException, QueryException {
     URI serverUri = cmd.getServerURI();
 
-    if (serverUri == null && cmd.isLocalOperation()) {
+    // check for server operations where we don't know the server
+    if (serverUri == null && !cmd.isLocalOperation()) {
       // no server URI, but not local. Get a connection for a null URI
+      // eg. select .... from <file:///...>
       Connection connection = connectionFactory.newConnection(serverUri);
       configureForTransaction(connection);
       return connection;
     }
     
-    return establishConnection(cmd.getServerURI());
+    // go the normal route for getting a connection for a given server location
+    return establishConnection(serverUri);
   }
 
+
   /**
    * Returns a connection to the server with the given URI.
    * NB: Not for general use. Available to ItqlInterpreterBean only to support
    * legacy requests to get a session.
-   * @param serverUri The URI for the server to get a connection to.
+   * @param serverUri The URI for the server to get a connection to. <code>null</code> for
+   *        Local operations that do not require a server.
    * @return A connection to the server, cached if available.
    * @throws ConnectionException It was not possible to create a connection to the described server.
    * @throws QueryException There is a transaction underway, but the new connection cannot turn off autocommit.
    */
   Connection establishConnection(URI serverUri) throws ConnectionException, QueryException {
+    // get a new connection, or use the local one for non-server operations
     Connection connection = (serverUri == null) ? localStateConnection : connectionFactory.newConnection(serverUri);
+    // update the connection if it needs to enter a current transaction
     configureForTransaction(connection);
     return connection;
   }
 
 
+  /**
+   * Set up the given connection for a current transaction, if one is active at the moment.
+   * @param connection The connection to configure. The dummy connection is not configured.
+   * @throws QueryException An error while setting up the connection for the transaction.
+   */
   private void configureForTransaction(Connection connection) throws QueryException {
     // If in a transaction, turn off autocommit - ignore the dummy connection
     if (inTransaction && connection.getAutoCommit() && connection != localStateConnection) {
+      assert !(connection instanceof DummyConnection);
       connection.setAutoCommit(false);
       assert !transConnections.contains(connection);
       transConnections.add(connection);
@@ -290,7 +309,7 @@
 
 
   /**
-   * Commits all connections that started on a transaction.  This oeprates directly
+   * Commits all connections that started on a transaction.  This operates directly
    * on all known transacted connections.
    * @deprecated This is only for manual transactions managed from ItqlInterpreterBean.
    * @throws QueryException One of the connections could not be successfully committed.
@@ -323,7 +342,38 @@
     }
   }
 
+
   /**
+   * This method wraps the simple loop of applying a command to all transaction connections.
+   * The wrapping is done to attempt the operation on all connections, despite exceptions
+   * being thrown.
+   * @param cmd The operation to execute on the transaction connections.
+   * @throws QueryException If there was an error applying the operation. If multiple
+   *         errors occur, then this will be the first one.
+   */
+  private void operateOnTxConnections(Command cmd) throws QueryException {
+    // used to record the first exception, if there is one.
+    QueryException qe = null;
+    
+    // perform operation on all transaction connections. Catch and log exceptions.
+    for (Connection c: transConnections) {
+      try {
+        cmd.execute(c);
+      } catch (QueryException e) {
+        if (qe == null) qe = e;
+        else logger.error("Discarding subsequent exception during operation: " + cmd.getClass().getSimpleName(), e);
+      } catch (Exception e) {
+        String opName = cmd.getClass().getSimpleName();
+        if (qe == null) qe = new QueryException("Unexpected exception during " + opName, e);
+        else logger.error("Discarding subsequent unexpected exception during operation: " + cmd.getClass().getSimpleName(), e);
+      }
+    }
+    // if an exception was recorded, then throw it
+    if (qe != null) throw qe;
+  }
+
+
+  /**
    * Enumerates a set of operations that can be performed to end a transaction.
    */
   private enum TxEndOp {
@@ -343,13 +393,13 @@
    */
   private void handleEndOfTx(TxEndOp op) throws QueryException {
     if (inTransaction) {
-      // Commit all outstanding transactions.
+      // Operate on all outstanding transactions.
       Iterator<Connection> c = transConnections.iterator();
       while (c.hasNext()) {
         Session s = c.next().getSession();
         if (s != null) op.execute(s);
         else logger.warn("Found transaction on a connection with no session.");
-        // now get rid of the commited connection
+        // now get rid of the commited/rolled-back connection
         c.remove();
       }
       // will only get here once all connections were successfully processed.




More information about the Mulgara-svn mailing list