[Mulgara-svn] r647 - in branches/mgr-73/src/jar: resolver-memory/java/org/mulgara/resolver/memory resolver-spi/java/org/mulgara/resolver/spi resolver-store/java/org/mulgara/resolver/store

ronald at mulgara.org ronald at mulgara.org
Mon Feb 18 13:44:00 UTC 2008


Author: ronald
Date: 2008-02-18 05:44:00 -0800 (Mon, 18 Feb 2008)
New Revision: 647

Added:
   branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/AbstractXAResource.java
Modified:
   branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolver.java
   branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolverFactory.java
   branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryXAResource.java
   branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/DummyXAResource.java
   branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java
   branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
   branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
Log:
XAResource implementation fixes:
 * added AbstractXAResource as a common superclass for XAResource
   implementations; it properly manages the resource-manager and transation
   lifecycles.
 * changed StatementStoreXAResource and MemoryXAResource to subclass new
   AbstractXAResource; this fixes current MemoryXAResource failures when using
   local transactions.


Modified: branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolver.java
===================================================================
--- branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolver.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolver.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -84,8 +84,10 @@
    */
   private final Set<Stating> statingSet;
 
-  private XAResolverSession xaResolverSession;
+  private final XAResource xares;
 
+  private final XAResolverSession xaResolverSession;
+
   //
   // Constructors
   //
@@ -105,34 +107,30 @@
    *   is {@link NodePool#NONE}
    */
   MemoryResolver(ResolverSession resolverSession,
-                 Resolver        systemResolver,
                  long            rdfType,
-                 long            systemModel,
                  URI             modelTypeURI,
                  Set<Stating>    statingSet)
       throws ResolverFactoryException {
-
-    // Validate "modelType" parameter
-    if (modelTypeURI == null) {
-      throw new IllegalArgumentException("Model type can't be null");
-    }
-
-    // Initialize fields
-    memoryModelTypeURI = modelTypeURI;
-    this.rdfType = rdfType;
-    this.resolverSession = resolverSession;
-    this.statingSet = statingSet;
-    this.xaResolverSession = null;
+    this(resolverSession, rdfType, modelTypeURI, statingSet, null, null);
   }
 
-
   MemoryResolver(long              rdfType,
-                 long              systemModel,
                  URI               modelTypeURI,
                  Set<Stating>      statingSet,
-                 XAResolverSession resolverSession)
+                 XAResolverSession resolverSession,
+                 ResolverFactory   resolverFactory)
       throws ResolverFactoryException {
-    
+    this(resolverSession, rdfType, modelTypeURI, statingSet, resolverSession,
+         resolverFactory);
+  }
+
+  private MemoryResolver(ResolverSession   resolverSession,
+                         long              rdfType,
+                         URI               modelTypeURI,
+                         Set<Stating>      statingSet,
+                         XAResolverSession xaResolverSession,
+                         ResolverFactory   resolverFactory)
+      throws ResolverFactoryException {
     // Validate "modelType" parameter
     if (modelTypeURI == null) {
       throw new IllegalArgumentException("Model type can't be null");
@@ -143,20 +141,21 @@
     this.rdfType = rdfType;
     this.resolverSession = resolverSession;
     this.statingSet = statingSet;
-    this.xaResolverSession = resolverSession;
+    this.xaResolverSession = xaResolverSession;
+
+    this.xares = (xaResolverSession != null) ?
+          new MemoryXAResource(10, xaResolverSession, resolverFactory) :
+          new DummyXAResource(10);
   }
 
 
+
   //
   // Methods implementing Resolver
   //
 
   public XAResource getXAResource() {
-    if (xaResolverSession != null) {
-      return new MemoryXAResource(10, xaResolverSession);
-    } else {
-      return new DummyXAResource(10);
-    }
+    return xares;
   }
 
 

Modified: branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolverFactory.java
===================================================================
--- branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolverFactory.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryResolverFactory.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -74,11 +74,6 @@
 
 
   /**
-   * The preallocated local node representing the system model (<code>#</code>).
-   */
-  private long systemModel;
-
-  /**
    * The {@link Stating}s which occur in all models created by resolvers
    * created by this factory.
    */
@@ -105,8 +100,6 @@
     rdfType = initializer.preallocate(new URIReferenceImpl(RDF.TYPE));
     initializer.preallocate(new URIReferenceImpl(modelTypeURI));
 
-    systemModel = initializer.getSystemModel();
-
     // Claim mulgara:MemoryModel
     initializer.addModelType(modelTypeURI, this);
 
@@ -136,7 +129,6 @@
 
   public void setDatabaseMetadata(DatabaseMetadata metadata) {
     rdfType = metadata.getRdfTypeNode();
-    systemModel = metadata.getSystemModelNode();
   }
 
 
@@ -222,9 +214,7 @@
   ) throws ResolverFactoryException {
     if (logger.isDebugEnabled()) logger.debug("Creating memory resolver");
     return new MemoryResolver(resolverSession,
-                              systemResolver,
                               rdfType,
-                              systemModel,
                               modelTypeURI,
                               statingSet);
   }
@@ -234,8 +224,9 @@
     assert sessionFactory != null;
     if (logger.isDebugEnabled()) logger.debug("Creating memory resolver factory");
     try {
-      return new MemoryResolver(rdfType, systemModel, modelTypeURI, statingSet,
-                                (XAResolverSession) sessionFactory.newWritableResolverSession());
+      return new MemoryResolver(rdfType, modelTypeURI, statingSet,
+                                (XAResolverSession) sessionFactory.newWritableResolverSession(),
+                                this);
     } catch (ResolverSessionFactoryException er) {
       throw new ResolverFactoryException("Failed to obtain a new ResolverSession", er);
     }

Modified: branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryXAResource.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-memory/java/org/mulgara/resolver/memory/MemoryXAResource.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -27,23 +27,15 @@
 
 package org.mulgara.resolver.memory;
 
-// Java 2 standard packages
-import java.util.*;
-import javax.transaction.xa.XAException;
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
-
-// Third party packages
-import org.apache.log4j.Logger;
-
-
+import org.mulgara.resolver.spi.AbstractXAResource;
+import org.mulgara.resolver.spi.AbstractXAResource.RMInfo;
+import org.mulgara.resolver.spi.AbstractXAResource.TxInfo;
+import org.mulgara.resolver.spi.ResolverFactory;
 import org.mulgara.store.xa.SimpleXAResource;
-import org.mulgara.store.xa.SimpleXAResourceException;
 import org.mulgara.store.xa.XAResolverSession;
 
 /**
- * A dummy implementation of the {@link XAResource} interface which logs the
- * calls made to it, but otherwise ignores them.
+ * Implements the XAResource for the {@link MemoryResolver}.
  *
  * @created 2004-05-12
  * @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a>
@@ -54,38 +46,8 @@
  *   Technoogies, Inc.</a>
  * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
  */
-
-public class MemoryXAResource implements XAResource
-{
-  /** Logger.  */
-  private static final Logger logger =
-    Logger.getLogger(MemoryXAResource.class.getName());
-
-  /**
-   * Map from keyed from the {@link Integer} value of the various flags
-   * defined in {@link XAResource} and mapping to the formatted name for that
-   * flag.
-   */
-  private final static Map flagMap = new HashMap();
-
-  static {
-    flagMap.put(new Integer(XAResource.TMENDRSCAN),   "TMENDRSCAN");
-    flagMap.put(new Integer(XAResource.TMFAIL),       "TMFAIL");
-    flagMap.put(new Integer(XAResource.TMJOIN),       "TMJOIN");
-    flagMap.put(new Integer(XAResource.TMONEPHASE),   "TMONEPHASE");
-    flagMap.put(new Integer(XAResource.TMRESUME),     "TMRESUME");
-    flagMap.put(new Integer(XAResource.TMSTARTRSCAN), "TMSTARTRSCAN");
-    flagMap.put(new Integer(XAResource.TMSUCCESS),    "TMSUCCESS");
-    flagMap.put(new Integer(XAResource.TMSUCCESS),    "TMSUSPEND");
-  }
-
-  /** The transaction timeout value in seconds.  */
-  private int transactionTimeout = 0;
-
-  private XAResolverSession session;
-  private boolean rollback;
-  private Xid xid;
-
+public class MemoryXAResource
+    extends AbstractXAResource<RMInfo<MemoryXAResource.MemoryTxInfo>,MemoryXAResource.MemoryTxInfo> {
   //
   // Constructor
   //
@@ -94,209 +56,56 @@
    * Construct a {@link MemoryXAResource} with a specified transaction timeout.
    *
    * @param transactionTimeout  transaction timeout period, in seconds
+   * @param session             the underlying resolver-session to use
+   * @param resolverFactory     the resolver-factory we belong to
    */
   public MemoryXAResource(int transactionTimeout,
-                          XAResolverSession session)
-  {
-    logger.debug("<init> Creating MemoryXAResource: " + this);
-    this.transactionTimeout = transactionTimeout * 100;
-    this.session = session;
-    this.rollback = false;
+                          XAResolverSession session,
+                          ResolverFactory resolverFactory) {
+    super(transactionTimeout, resolverFactory, newTxInfo(session));
   }
 
-  //
-  // Methods implementing XAResource
-  //
-
-  public void start(Xid xid, int flags) throws XAException
-  {
-    logger.debug("Start " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
-    switch (flags) {
-      case XAResource.TMRESUME:
-        logger.debug("Resuming transaction on " + System.identityHashCode(xid));
-        break;
-      case XAResource.TMNOFLAGS:
-        try {
-          session.refresh(new SimpleXAResource[] {});
-          this.xid = xid;
-        } catch (SimpleXAResourceException es) {
-          logger.warn("Failed to refresh phases", es);
-          throw new XAException(XAException.XAER_RMFAIL);
-        }
-        break;
-      default:
-        rollback = true;
-        logger.error("Unrecognised flags in start: " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
-        throw new XAException(XAException.XAER_INVAL);
-    }
+  protected RMInfo<MemoryTxInfo> newResourceManager() {
+    return new RMInfo<MemoryTxInfo>();
   }
 
-  public int prepare(Xid xid) throws XAException
-  {
-    logger.debug("Prepare " + System.identityHashCode(xid));
-    logger.debug("Prepare always returns XA_OK, never XA_RDONLY");
-
-    if (rollback) {
-      logger.error("Attempting to prepare in failed transaction");
-      throw new XAException(XAException.XA_RBROLLBACK);
-    }
-    if (!xid.equals(this.xid)) {
-      logger.error("Attempting to prepare unknown transaction.");
-      throw new XAException(XAException.XAER_NOTA);
-    }
-
-    try {
-      session.prepare();
-    } catch (SimpleXAResourceException es) {
-      logger.warn("Attempt to prepare store failed", es);
-      throw new XAException(XAException.XA_RBROLLBACK);
-    }
-
-    return XA_OK;
+  private static MemoryTxInfo newTxInfo(XAResolverSession session) {
+    MemoryTxInfo ti = new MemoryTxInfo();
+    ti.session = session;
+    return ti;
   }
 
-  public void commit(Xid xid, boolean onePhase) throws XAException
-  {
-    logger.debug("Commit xid=" + System.identityHashCode(xid) + " onePhase=" + onePhase);
-    if (rollback) {
-      logger.error("Attempting to commit in failed transaction");
-      throw new XAException(XAException.XA_RBROLLBACK);
-    }
-    if (!xid.equals(this.xid)) {
-      logger.error("Attempting to commit unknown transaction.");
-      throw new XAException(XAException.XAER_NOTA);
-    }
+  //
+  // Methods implementing XAResource
+  //
 
-    try {
-      if (onePhase) {
-        // Check return value is XA_OK.
-        prepare(xid);
-      }
-    } catch (Throwable th) {
-      this.rollback = true;
-      logger.error("Attempt to prepare in onePhaseCommit failed.", th);
-      throw new XAException(XAException.XA_RBROLLBACK);
+  protected void doStart(MemoryTxInfo tx, int flags, boolean isNew) throws Exception {
+    if (flags == TMNOFLAGS || flags == TMJOIN) {
+      tx.session.refresh(new SimpleXAResource[] {});
     }
-
-    try {
-      session.commit();
-    } catch (Throwable th) {
-      // This is a serious problem since the database is now in an
-      // inconsistent state.
-      // Make sure the exception is logged.
-      logger.fatal("Failed to commit resource in transaction " + xid, th);
-      throw new XAException(XAException.XAER_RMERR);
-    }
   }
 
-  public void end(Xid xid, int flags) throws XAException
-  {
-    logger.debug("End xid=" + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+  protected void doEnd(MemoryTxInfo tx, int flags) {
   }
 
-  public void forget(Xid xid) throws XAException
-  {
-    logger.debug("Forget xid=" + System.identityHashCode(xid));
+  protected int doPrepare(MemoryTxInfo tx) throws Exception {
+    tx.session.prepare();
+    return XA_OK;
   }
 
-  public int getTransactionTimeout() throws XAException
-  {
-    logger.debug("Get transaction timeout: " + transactionTimeout);
-    return transactionTimeout;
+  protected void doCommit(MemoryTxInfo tx) throws Exception {
+    tx.session.commit();
   }
 
-  public boolean isSameRM(XAResource xaResource) throws XAException
-  {
-    logger.debug("Is same resource manager? " + (xaResource == this) + " :: " + xaResource + " on " + this);
-    return xaResource == this;
+  protected void doRollback(MemoryTxInfo tx) throws Exception {
+    tx.session.rollback();
   }
 
-  public Xid[] recover(int flag) throws XAException
-  {
-    logger.debug("Recover flag=" + formatFlags(flag));
-    throw new XAException(XAException.XAER_RMERR);
+  protected void doForget(MemoryTxInfo tx) {
   }
 
-  public void rollback(Xid xid) throws XAException
-  {
-    logger.debug("Rollback " + System.identityHashCode(xid));
-
-    boolean fatalError = false;
-
-    if (!xid.equals(this.xid)) {
-      logger.error("Attempting to rollback unknown transaction.");
-      fatalError = true;
-    }
-
-    try {
-      session.rollback();
-    } catch (Throwable th) {
-      // This is a serious problem since the database is now in an
-      // inconsistent state.
-      // Make sure the exception is logged.
-      logger.fatal("Failed to rollback resource in transaction " + xid, th);
-      fatalError = true;
-    }
-
-    if (fatalError) {
-      logger.fatal("Fatal error occured while rolling back transaction " + xid + " in manager for " + this.xid);
-      throw new XAException(XAException.XAER_RMERR);
-    }
+  static class MemoryTxInfo extends TxInfo {
+    /** the underlying resolver-session to use */
+    public XAResolverSession session;
   }
-
-  public boolean setTransactionTimeout(int transactionTimeout)
-    throws XAException
-  {
-    logger.debug("Set transaction timeout: " + transactionTimeout);
-    this.transactionTimeout = transactionTimeout;
-    return true;
-  }
-
-  //
-  // Internal methods
-  //
-
-  /**
-   * Format bitmasks defined by {@link XAResource}.
-   *
-   * @param flags  a bitmask composed from the constants defined in
-   *   {@link XAResource}
-   * @return a formatted representation of the <var>flags</var>
-   */
-  private static String formatFlags(int flags)
-  {
-    // Short-circuit evaluation if we've been explicitly passed no flags
-    if (flags == XAResource.TMNOFLAGS) {
-      return "TMNOFLAGS";
-    }
-
-    StringBuffer buffer = new StringBuffer();
-
-    // Add any flags that are present
-    for (Iterator i = flagMap.entrySet().iterator(); i.hasNext(); ) {
-      Map.Entry entry = (Map.Entry)i.next();
-      int entryFlag = ((Integer)entry.getKey()).intValue();
-
-      // If this flag is present, add it to the formatted output and remove
-      // from the bitmask
-      if ((entryFlag & flags) == entryFlag) {
-        if (buffer.length() > 0) {
-          buffer.append(",");
-        }
-        buffer.append(entry.getValue());
-        flags &= ~entryFlag;
-      }
-    }
-
-    // We would expect to have removed all flags by this point
-    // If there's some unknown flag we've missed, format it as hexadecimal
-    if (flags != 0) {
-      if (buffer.length() > 0) {
-        buffer.append(",");
-      }
-      buffer.append("0x").append(Integer.toHexString(flags));
-    }
-
-    return buffer.toString();
-  }
 }

Added: branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/AbstractXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/AbstractXAResource.java	                        (rev 0)
+++ branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/AbstractXAResource.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -0,0 +1,446 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (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.mozilla.org/MPL/
+ *
+ * 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.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+
+package org.mulgara.resolver.spi;
+
+// Java 2 standard packages
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+// Third party packages
+import org.apache.log4j.Logger;
+
+/**
+ * A skeleton XAResource implementation. This handles the basic
+ * resource-manager and transaction management and ensures correct {@link
+ * #isSameRM} implementation. Subclasses must implement the actual
+ * functionality in the {@link #doStart}, {@link #doPrepare}, {@link
+ * #doCommit}, and {@link #doRollback} methods.
+ *
+ * @created 2008-02-16
+ * @author Ronald Tschalär
+ * @licence Open Software License v3.0
+ */
+public abstract class AbstractXAResource<R extends AbstractXAResource.RMInfo<T>,T extends AbstractXAResource.TxInfo>
+    extends DummyXAResource {
+  /** Logger.  */
+  private static final Logger logger =
+    Logger.getLogger(AbstractXAResource.class.getName());
+
+  protected static final Map<ResolverFactory,RMInfo<? extends TxInfo>> resourceManagers =
+    new WeakHashMap<ResolverFactory,RMInfo<? extends TxInfo>>();
+
+  protected final R resourceManager;
+  protected final T tmpTxInfo;
+
+
+  //
+  // Constructor
+  //
+
+  /**
+   * Construct an XAResource.
+   *
+   * @param transactionTimeout  transaction timeout period, in seconds
+   * @param resolverFactory     the resolver-factory we belong to
+   * @param txInfo              the initial transaction-info
+   */
+  public AbstractXAResource(int transactionTimeout,
+                            ResolverFactory resolverFactory,
+                            T txInfo) {
+    super(transactionTimeout);
+
+    synchronized (resourceManagers) {
+      @SuppressWarnings("unchecked")
+      R rmgr = (R) resourceManagers.get(resolverFactory);
+      if (rmgr == null)
+        resourceManagers.put(resolverFactory, rmgr = newResourceManager());
+      this.resourceManager = rmgr;
+    }
+
+    this.tmpTxInfo = txInfo;
+  }
+
+  /**
+   * Create a new resource-manager instance - invoked only from the
+   * constructor and only when no resource-manager instance exists for the
+   * given resolver-factory.
+   */
+  protected abstract R newResourceManager();
+
+  //
+  // Methods implementing XAResource
+  //
+
+  public void start(Xid xid, int flags) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Start xid=" + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+      logger.debug("xid.format=" + xid.getFormatId() + " xid.gblTxId=" + Arrays.toString(xid.getGlobalTransactionId()) + " xid.brnchQual=" + Arrays.toString(xid.getBranchQualifier()));
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    boolean isNew = false;
+
+    switch (flags) {
+      case XAResource.TMRESUME:
+        if (tx == null) {
+          logger.error("Attempting to resume unknown transaction.");
+          throw new XAException(XAException.XAER_NOTA);
+        }
+        if (logger.isDebugEnabled()) {
+          logger.debug("Resuming transaction on xid=" + System.identityHashCode(xid));
+        }
+        break;
+
+      case XAResource.TMNOFLAGS:
+        if (tx != null) {
+          logger.warn("Received plain start for existing tx");
+          logger.warn("xid.format=" + xid.getFormatId() + " xid.gblTxId=" + Arrays.toString(xid.getGlobalTransactionId()) + " xid.brnchQual=" + Arrays.toString(xid.getBranchQualifier()));
+          throw new XAException(XAException.XAER_DUPID);
+        }
+        // fallthrough
+
+      case XAResource.TMJOIN:
+        if (tx == null) {
+          resourceManager.transactions.put(new XidWrapper(xid), tx = tmpTxInfo);
+          isNew = true;
+        }
+        break;
+
+      default:
+        // XXX: is this correct? Or should we actually roll back here?
+        if (tx != null)
+          tx.rollback = true;
+        logger.error("Unrecognised flags in start: xid=" + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+        throw new XAException(XAException.XAER_INVAL);
+    }
+
+    try {
+      doStart(tx, flags, isNew);
+    } catch (Throwable t) {
+      logger.warn("Failed to do start", t);
+      resourceManager.transactions.remove(new XidWrapper(xid));
+      throw new XAException(XAException.XAER_RMFAIL);
+    }
+  }
+
+  public void end(Xid xid, int flags) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("End xid=" + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    if (tx == null) {
+      logger.error("Attempting to end unknown transaction.");
+      throw new XAException(XAException.XAER_NOTA);
+    }
+
+    try {
+      doEnd(tx, flags);
+    } catch (Throwable t) {
+      logger.warn("Failed to do end", t);
+      resourceManager.transactions.remove(new XidWrapper(xid));
+      throw new XAException(XAException.XAER_RMFAIL);
+    }
+  }
+
+  public int prepare(Xid xid) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Prepare xid=" + System.identityHashCode(xid));
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    if (tx == null) {
+      logger.error("Attempting to prepare unknown transaction.");
+      throw new XAException(XAException.XAER_NOTA);
+    }
+
+    if (tx.rollback) {
+      logger.info("Attempting to prepare in failed transaction");
+      rollback(xid);
+      throw new XAException(XAException.XA_RBROLLBACK);
+    }
+
+    try {
+      int sts = doPrepare(tx);
+      if (sts == XA_RDONLY)
+        resourceManager.transactions.remove(new XidWrapper(xid));
+      return sts;
+    } catch (Throwable t) {
+      logger.warn("Attempt to prepare failed", t);
+      rollback(xid);
+      throw new XAException(XAException.XA_RBROLLBACK);
+    }
+  }
+
+  public void commit(Xid xid, boolean onePhase) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Commit xid=" + System.identityHashCode(xid) + " onePhase=" + onePhase);
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    if (tx == null) {
+      logger.error("Attempting to commit unknown transaction.");
+      throw new XAException(XAException.XAER_NOTA);
+    }
+    if (tx.rollback) {
+      logger.error("Attempting to commit in failed transaction");
+      rollback(xid);
+      throw new XAException(XAException.XA_RBROLLBACK);
+    }
+
+    try {
+      if (onePhase) {
+        int sts = doPrepare(tx);
+        if (sts == XA_RDONLY) {
+          resourceManager.transactions.remove(new XidWrapper(xid));
+          return;
+        }
+      }
+    } catch (Throwable th) {
+      logger.error("Attempt to prepare in onePhaseCommit failed.", th);
+      rollback(xid);
+      throw new XAException(XAException.XA_RBROLLBACK);
+    }
+
+    boolean clean = true;
+    try {
+      doCommit(tx);
+    } catch (XAException xae) {
+      if (isHeuristic(xae)) {
+        clean = false;
+      }
+      throw xae;
+    } catch (Throwable th) {
+      // This is a serious problem since the database is now in an
+      // inconsistent state.
+      // Make sure the exception is logged.
+      logger.fatal("Failed to commit resource in transaction " + xid, th);
+      throw new XAException(XAException.XAER_RMERR);
+    } finally {
+      if (clean) {
+        resourceManager.transactions.remove(new XidWrapper(xid));
+      }
+    }
+  }
+
+  public void rollback(Xid xid) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Rollback xid=" + System.identityHashCode(xid));
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    if (tx == null) {
+      logger.error("Attempting to rollback unknown transaction.");
+      throw new XAException(XAException.XAER_NOTA);
+    }
+
+    boolean clean = true;
+    try {
+      doRollback(tx);
+    } catch (XAException xae) {
+      if (isHeuristic(xae)) {
+        clean = false;
+      }
+      throw xae;
+    } catch (Throwable th) {
+      // This is a serious problem since the database is now in an
+      // inconsistent state.
+      // Make sure the exception is logged.
+      logger.fatal("Failed to rollback resource in transaction " + xid, th);
+      throw new XAException(XAException.XAER_RMERR);
+    } finally {
+      if (clean) {
+        resourceManager.transactions.remove(new XidWrapper(xid));
+      }
+    }
+  }
+
+  public void forget(Xid xid) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Forget xid=" + System.identityHashCode(xid));
+    }
+
+    T tx = resourceManager.transactions.get(new XidWrapper(xid));
+    if (tx == null) {
+      logger.error("Attempting to forget unknown transaction.");
+      throw new XAException(XAException.XAER_NOTA);
+    }
+
+    boolean clean = true;
+    try {
+      doForget(tx);
+    } catch (XAException xae) {
+      if (xae.errorCode == XAException.XAER_RMERR) {
+        clean = false;
+      }
+      throw xae;
+    } catch (Throwable th) {
+      logger.error("Failed to forget transaction " + xid, th);
+      clean = false;
+      throw new XAException(XAException.XAER_RMERR);
+    } finally {
+      if (clean) {
+        resourceManager.transactions.remove(new XidWrapper(xid));
+      }
+    }
+  }
+
+  public Xid[] recover(int flag) throws XAException {
+    if (logger.isDebugEnabled()) {
+      logger.debug("Recover flag=" + formatFlags(flag));
+    }
+
+    throw new XAException(XAException.XAER_RMERR);
+  }
+
+  public boolean isSameRM(XAResource xaResource) throws XAException {
+    boolean same = (xaResource instanceof AbstractXAResource) &&
+      ((AbstractXAResource)xaResource).resourceManager == resourceManager;
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("Is same resource manager? " + same + " :: " + xaResource + " on " + this);
+    }
+
+    return same;
+  }
+
+  /** 
+   * Invoked on start with valid flags and tx state.
+   * 
+   * @param tx     the transaction being started; always non-null
+   * @param flags  one of TMNOFLAGS, TMRESUME, or TMJOIN
+   * @param isNew  true if <var>tx</var> was created as part of this start()
+   * @throws Exception 
+   */
+  protected abstract void doStart(T tx, int flags, boolean isNew) throws Exception;
+
+  /** 
+   * Invoked on end().
+   * 
+   * @param tx     the transaction being ended; always non-null
+   * @param flags  one of TMSUCCESS, TMFAIL, or TMSUSPEND
+   * @throws Exception 
+   */
+  protected abstract void doEnd(T tx, int flags) throws Exception;
+
+  /** 
+   * Invoked on prepare() or commit(onePhase=true).
+   * 
+   * @param tx  the transaction being prepared; always non-null
+   * @return XA_OK or XA_RDONLY
+   * @throws Exception 
+   */
+  protected abstract int doPrepare(T tx) throws Exception;
+
+  /** 
+   * Invoked on commit().
+   * 
+   * @param tx  the transaction being committed; always non-null
+   * @throws Exception 
+   */
+  protected abstract void doCommit(T tx) throws Exception;
+
+  /** 
+   * Invoked on (explicit or implicit) rollback().
+   * 
+   * @param tx  the transaction being rolled back; always non-null
+   * @throws Exception 
+   */
+  protected abstract void doRollback(T tx) throws Exception;
+
+  /** 
+   * Invoked on forget().
+   * 
+   * @param tx  the transaction to forget; always non-null
+   * @throws Exception 
+   */
+  protected abstract void doForget(T tx) throws Exception;
+
+  private static boolean isHeuristic(XAException xae) {
+    return (xae.errorCode == XAException.XA_HEURHAZ || xae.errorCode == XAException.XA_HEURCOM ||
+            xae.errorCode == XAException.XA_HEURRB  || xae.errorCode == XAException.XA_HEURMIX);
+  }
+
+
+  /** The resource-manager info */
+  public static class RMInfo<T extends TxInfo> {
+    /** the list of active transactions */
+    public final Map<XidWrapper,T> transactions =
+      Collections.synchronizedMap(new HashMap<XidWrapper,T>());
+  }
+
+  /** The info pertaining to a single transaction */
+  public static class TxInfo {
+    /** true if this transaction has been marked for rollback */
+    public boolean rollback;
+  }
+
+  /**
+   * Xid-wrapper that implements hashCode() and equals(). JTA does not require
+   * Xid's to implement hashCode() and equals(), so in order to be able to use
+   * them as keys in a map we need to wrap them with something that implements
+   * them based on the individual fields of the Xid.
+   */
+  public static class XidWrapper {
+    private final Xid xid;
+    private final int hash;
+
+    public XidWrapper(Xid xid) {
+      this.xid = xid;
+      this.hash = Arrays.hashCode(xid.getBranchQualifier());
+    }
+
+    public int hashCode() {
+      return hash;
+    }
+
+    public boolean equals(Object other) {
+      Xid o;
+
+      if (other instanceof XidWrapper) {
+        o = ((XidWrapper)other).xid;
+      } else if (other instanceof Xid) {
+        o = (Xid)other;
+      } else {
+        return false;
+      }
+
+      if (o == xid)
+        return true;
+      return o.getFormatId() == xid.getFormatId() &&
+             Arrays.equals(o.getGlobalTransactionId(), xid.getGlobalTransactionId()) &&
+             Arrays.equals(o. getBranchQualifier(), xid. getBranchQualifier());
+    }
+  }
+}


Property changes on: branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/AbstractXAResource.java
___________________________________________________________________
Name: svn:keywords
   + Id HeadURL Revision
Name: svn:eol-style
   + native

Modified: branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/DummyXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/DummyXAResource.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-spi/java/org/mulgara/resolver/spi/DummyXAResource.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -61,7 +61,7 @@
    * defined in {@link XAResource} and mapping to the formatted name for that
    * flag.
    */
-  private final static Map<Integer,String> flagMap = new HashMap<Integer,String>();
+  protected final static Map<Integer,String> flagMap = new HashMap<Integer,String>();
 
   static {
     flagMap.put(new Integer(XAResource.TMENDRSCAN),   "TMENDRSCAN");
@@ -75,7 +75,7 @@
   }
 
   /** The transaction timeout value in seconds.  */
-  private int transactionTimeout = 0;
+  protected int transactionTimeout = 0;
 
   //
   // Constructor
@@ -88,7 +88,7 @@
    */
   public DummyXAResource(int transactionTimeout) {
     if (logger.isDebugEnabled()) {
-      logger.debug("Creating DummyXAResource with timeout " + transactionTimeout);
+      logger.debug("Creating " + getClass().getName() + " with timeout " + transactionTimeout);
     }
     this.transactionTimeout = transactionTimeout;
   }
@@ -173,7 +173,7 @@
    * @param flags  a bitmask composed from the constants defined in {@link XAResource}
    * @return a formatted representation of the <var>flags</var>
    */
-  private static final String formatFlags(int flags) {
+  protected static final String formatFlags(int flags) {
     // Short-circuit evaluation if we've been explicitly passed no flags
     if (flags == XAResource.TMNOFLAGS) {
       return "TMNOFLAGS";

Modified: branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java
===================================================================
--- branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -131,7 +131,8 @@
                          long systemModel,
                          URI modelTypeURI,
                          XAResolverSession resolverSession,
-                         XAStatementStore statementStore)
+                         XAStatementStore statementStore,
+                         ResolverFactory resolverFactory)
       throws IllegalArgumentException, ResolverFactoryException
   {
     // Validate parameters
@@ -153,7 +154,8 @@
     this.xaresource = new StatementStoreXAResource(
         10,  // transaction timeout in seconds
         resolverSession,
-        new SimpleXAResource[] { statementStore });
+        new SimpleXAResource[] { statementStore },
+        resolverFactory);
   }
 
   StatementStoreResolver(Resolver systemResolver,
@@ -161,7 +163,8 @@
                          long systemModel,
                          URI modelTypeURI,
                          XAResolverSession resolverSession,
-                         XAStatementStore statementStore)
+                         XAStatementStore statementStore,
+                         ResolverFactory resolverFactory)
       throws IllegalArgumentException, ResolverFactoryException
   {
     // Validate parameters
@@ -182,7 +185,8 @@
     this.xaresource = new StatementStoreXAResource(
         10,  // transaction timeout in seconds
         resolverSession,
-        new SimpleXAResource[] { statementStore });
+        new SimpleXAResource[] { statementStore },
+        resolverFactory);
   }
 
 
@@ -374,7 +378,7 @@
                                                + resolverSession.globalize(statements.getObject()) + " "
                                                + resolverSession.globalize(model) + "]", e);
       } catch (Exception eg) {
-	      throw new ResolverException("Failed to globalize in debug", eg);
+        throw new ResolverException("Failed to globalize in debug", eg);
       }
       throw new ResolverException("Couldn't make statement " + occurs + " in " + model, e);
     } catch (TuplesException e) {

Modified: branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
===================================================================
--- branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -195,7 +195,8 @@
               : (XAResolverSession) resolverSessionFactory.newReadOnlyResolverSession(),
           allowWrites
               ? statementStore.newWritableStatementStore()
-              : statementStore.newReadOnlyStatementStore());
+              : statementStore.newReadOnlyStatementStore(),
+          this);
     } catch (ResolverSessionFactoryException er) {
       throw new ResolverFactoryException(
           "Failed to obtain a new ResolverSession", er);
@@ -216,7 +217,8 @@
           :
           (XAResolverSession) resolverSessionFactory.newReadOnlyResolverSession(),
           allowWrites ? statementStore.newWritableStatementStore()
-          : statementStore.newReadOnlyStatementStore());
+          : statementStore.newReadOnlyStatementStore(),
+          this);
     } catch (ResolverSessionFactoryException er) {
       throw new ResolverFactoryException(
           "Failed to obtain a new ResolverSession", er);

Modified: branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java	2008-02-18 13:37:11 UTC (rev 646)
+++ branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java	2008-02-18 13:44:00 UTC (rev 647)
@@ -28,22 +28,22 @@
 package org.mulgara.resolver.store;
 
 // Java 2 standard packages
-import java.util.*;
-import javax.transaction.xa.XAException;
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
+import java.util.HashSet;
+import java.util.Set;
 
 // Third party packages
 import org.apache.log4j.Logger;
 
-
+import org.mulgara.resolver.spi.AbstractXAResource;
+import org.mulgara.resolver.spi.AbstractXAResource.RMInfo;
+import org.mulgara.resolver.spi.AbstractXAResource.TxInfo;
+import org.mulgara.resolver.spi.ResolverFactory;
 import org.mulgara.store.xa.SimpleXAResource;
 import org.mulgara.store.xa.SimpleXAResourceException;
 import org.mulgara.store.xa.XAResolverSession;
 
 /**
- * A dummy implementation of the {@link XAResource} interface which logs the
- * calls made to it, but otherwise ignores them.
+ * Implements the XAResource for the {@link StatementStoreResolver}.
  *
  * @created 2004-05-12
  * @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a>
@@ -55,40 +55,15 @@
  * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
  */
 
-public class StatementStoreXAResource implements XAResource
-{
+public class StatementStoreXAResource
+    extends AbstractXAResource<RMInfo<StatementStoreXAResource.StatementStoreTxInfo>, StatementStoreXAResource.StatementStoreTxInfo> {
   /** Logger.  */
   private static final Logger logger =
     Logger.getLogger(StatementStoreXAResource.class.getName());
 
-  /**
-   * Map from keyed from the {@link Integer} value of the various flags
-   * defined in {@link XAResource} and mapping to the formatted name for that
-   * flag.
-   */
-  private final static Map flagMap = new HashMap();
-
-  static {
-    flagMap.put(new Integer(XAResource.TMENDRSCAN),   "TMENDRSCAN");
-    flagMap.put(new Integer(XAResource.TMFAIL),       "TMFAIL");
-    flagMap.put(new Integer(XAResource.TMJOIN),       "TMJOIN");
-    flagMap.put(new Integer(XAResource.TMONEPHASE),   "TMONEPHASE");
-    flagMap.put(new Integer(XAResource.TMRESUME),     "TMRESUME");
-    flagMap.put(new Integer(XAResource.TMSTARTRSCAN), "TMSTARTRSCAN");
-    flagMap.put(new Integer(XAResource.TMSUCCESS),    "TMSUCCESS");
-    flagMap.put(new Integer(XAResource.TMSUSPEND),    "TMSUSPEND");
-  }
-
-  /** The transaction timeout value in seconds.  */
-  private int transactionTimeout = 0;
-
-  private SimpleXAResource[] resources;
-  private XAResolverSession session;
-  private boolean rollback;
-  private Xid xid;
   // Used to prevent multiple calls to prepare on the store layer.
   // Set of session's that have been prepared.
-  private static Set preparing = new HashSet();
+  public static Set<XAResolverSession> preparing = new HashSet<XAResolverSession>();
 
   //
   // Constructor
@@ -98,296 +73,127 @@
    * Construct a {@link StatementStoreXAResource} with a specified transaction timeout.
    *
    * @param transactionTimeout  transaction timeout period, in seconds
+   * @param session             the underlying resolver-session to use
+   * @param resources           
+   * @param resolverFactory     the resolver-factory we belong to
    */
   public StatementStoreXAResource(int transactionTimeout,
                                   XAResolverSession session,
-                                  SimpleXAResource[] resources)
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("<init> Creating StatementStoreXAResource: " + this);
-    }
-    this.transactionTimeout = transactionTimeout * 100;
-    this.resources = resources;
-    this.session = session;
+                                  SimpleXAResource[] resources,
+                                  ResolverFactory resolverFactory) {
+    super(transactionTimeout, resolverFactory, newTxInfo(session, resources));
   }
 
+  protected RMInfo<StatementStoreTxInfo> newResourceManager() {
+    return new RMInfo<StatementStoreTxInfo>();
+  }
+
+  private static StatementStoreTxInfo newTxInfo(XAResolverSession session,
+                                                SimpleXAResource[] resources) {
+    StatementStoreTxInfo ti = new StatementStoreTxInfo();
+    ti.session = session;
+    ti.resources = resources;
+    return ti;
+  }
+
   //
   // Methods implementing XAResource
   //
 
-  public void start(Xid xid, int flags) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Start " + System.identityHashCode(xid) + " flags=" +
-      formatFlags(flags), new Throwable());
-    } 
-    switch (flags) {
-      case XAResource.TMNOFLAGS:
-        try {
-          session.refresh(resources);
-          this.xid = xid;
-          this.rollback = false;
-        } catch (SimpleXAResourceException es) {
-          logger.error("Failed to obtain phases", es);
-          throw new XAException(XAException.XAER_RMFAIL);
-        }
-        break;
-      case XAResource.TMRESUME:
-        if (!xid.equals(this.xid)) {
-          logger.error("Attempt to resume resource in wrong transaction.");
-          throw new XAException(XAException.XAER_INVAL);
-        }
-        break;
-      case XAResource.TMJOIN:
-        if (!xid.equals(this.xid)) {
-          logger.error("Attempt to join with wrong transaction.");
-          throw new XAException(XAException.XAER_INVAL);
-        }
-        break;
-      default:  // Currently fall-through.
-        rollback = true;
-        logger.warn("Unrecognised flags in start: " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
-        if (logger.isDebugEnabled()) {
-          logger.debug("This XAResource = " + System.identityHashCode(this.xid));
-        }
-        throw new XAException(XAException.XAER_INVAL);
+  protected void doStart(StatementStoreTxInfo tx, int flags, boolean isNew) throws Exception {
+    if (flags == TMNOFLAGS) {
+      tx.session.refresh(tx.resources);
     }
   }
 
-  public int prepare(Xid xid) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("XAResource " + this + " Prepare " + System.identityHashCode(xid) + " With Session: " + System.identityHashCode(session));
-    }
+  protected void doEnd(StatementStoreTxInfo tx, int flags) {
+  }
 
-    if (rollback) {
-      logger.error("Attempting to prepare in failed transaction");
-      throw new XAException(XAException.XA_RBROLLBACK);
-    }
-    if (!xid.equals(this.xid)) {
-      logger.error("Attempting to prepare unknown transaction.");
-      throw new XAException(XAException.XAER_NOTA);
-    }
-    synchronized(preparing) {
-      if (preparing.contains(session)) {
+  protected int doPrepare(StatementStoreTxInfo tx) throws Exception {
+    synchronized (preparing) {
+      if (preparing.contains(tx.session)) {
         return XA_OK;
       } else {
-        preparing.add(session);
+        preparing.add(tx.session);
       }
     }
 
     try {
-      session.prepare();
+      tx.session.prepare();
     } catch (SimpleXAResourceException es) {
-      logger.warn("Attempt to prepare store failed", es);
-      synchronized(preparing) {
-        preparing.remove(session);
+      synchronized (preparing) {
+        preparing.remove(tx.session);
       }
-      throw new XAException(XAException.XA_RBROLLBACK);
+      throw es;
     }
 
     return XA_OK;
   }
 
-  public void commit(Xid xid, boolean onePhase) throws XAException
-  {
+  protected void doCommit(StatementStoreTxInfo tx) throws Exception {
     try {
-      if (logger.isDebugEnabled()) {
-        logger.debug("XAResource " + this + " Commit xid=" + System.identityHashCode(xid) + " onePhase=" + onePhase + " session=" + System.identityHashCode(session));
-      }
-      if (rollback) {
-        logger.error("Attempting to commit in failed transaction");
-        throw new XAException(XAException.XA_RBROLLBACK);
-      }
-      if (!xid.equals(this.xid)) {
-        logger.error("Attempting to commit unknown transaction.");
-        throw new XAException(XAException.XAER_NOTA);
-      }
-      try {
-        if (onePhase) {
-          // Currently prepare only returns XA_OK, and throws an exception on failure.
-          prepare(xid);
-        }
-      } catch (Throwable th) {
-        this.rollback = true;
-        logger.error("Attempt to prepare in onePhaseCommit failed.", th);
-        throw new XAException(XAException.XA_RBROLLBACK);
-      }
-
-      try {
-        session.commit();
-      } catch (Throwable th) {
-        // This is a serious problem since the database is now in an
-        // inconsistent state.
-        // Make sure the exception is logged.
-        logger.fatal("Failed to commit resource in transaction " + xid, th);
-        throw new XAException(XAException.XAER_RMERR);
-      }
+      tx.session.commit();
     } finally {
-      cleanup("commit");
+      cleanup("commit", tx);
     }
-
   }
 
-  public void end(Xid xid, int flags) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("End " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
-    }
-  }
-
-  public void forget(Xid xid) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Forget xid=" + System.identityHashCode(xid));
-    }
+  protected void doForget(StatementStoreTxInfo tx) throws Exception {
     try {
-      synchronized(preparing) {
-        if (preparing.contains(session)) {
-          rollback(xid);
+      synchronized (preparing) {
+        if (preparing.contains(tx.session)) {
+          doRollback(tx);
         }
       }
     } finally {
-      cleanup("forget");
+      cleanup("forget", tx);
     }
   }
 
-  public int getTransactionTimeout() throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Get transaction timeout: " + transactionTimeout);
-    }
-    return transactionTimeout;
-  }
-
-  public boolean isSameRM(XAResource xaResource) throws XAException
-  {
-    return xaResource == this;
-  }
-
-  public Xid[] recover(int flag) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Recover flag=" + formatFlags(flag));
-    }
-    throw new XAException(XAException.XAER_RMERR);
-  }
-
-  public void rollback(Xid xid) throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Rollback " + System.identityHashCode(xid));
-    }
-
-    boolean fatalError = false;
-
-    if (!xid.equals(this.xid)) {
-      logger.error("Attempting to rollback unknown transaction.");
-      fatalError = true;
-    }
-
+  protected void doRollback(StatementStoreTxInfo tx) throws Exception {
     try {
-      if (logger.isDebugEnabled()) {
-        logger.debug("Rolling back phase");
-      }
-      session.rollback();
-    } catch (Throwable th) {
-      // This is a serious problem since the database is now in an
-      // inconsistent state.
-      // Make sure the exception is logged.
-      logger.fatal("Failed to rollback resource in transaction " + xid, th);
-      fatalError = true;
+      tx.session.rollback();
     } finally {
-      cleanup("rollback");
+      cleanup("rollback", tx);
     }
-
-    if (fatalError) {
-      logger.fatal("Fatal error occured while rolling back transaction " + xid + " in manager for " + this.xid);
-      throw new XAException(XAException.XAER_RMERR);
-    }
   }
 
-  public boolean setTransactionTimeout(int transactionTimeout)
-    throws XAException
-  {
-    if (logger.isDebugEnabled()) {
-      logger.debug("Set transaction timeout: " + transactionTimeout);
-    }
-    this.transactionTimeout = transactionTimeout;
-    return true;
-  }
-
   //
   // Internal methods
   //
 
-  /**
-   * Format bitmasks defined by {@link XAResource}.
-   *
-   * @param flags  a bitmask composed from the constants defined in
-   *   {@link XAResource}
-   * @return a formatted representation of the <var>flags</var>
-   */
-  private static String formatFlags(int flags)
-  {
-    // Short-circuit evaluation if we've been explicitly passed no flags
-    if (flags == XAResource.TMNOFLAGS) {
-      return "TMNOFLAGS";
-    }
-
-    StringBuffer buffer = new StringBuffer();
-
-    // Add any flags that are present
-    for (Iterator i = flagMap.entrySet().iterator(); i.hasNext(); ) {
-      Map.Entry entry = (Map.Entry)i.next();
-      int entryFlag = ((Integer)entry.getKey()).intValue();
-
-      // If this flag is present, add it to the formatted output and remove
-      // from the bitmask
-      if ((entryFlag & flags) == entryFlag) {
-        if (buffer.length() > 0) {
-          buffer.append(",");
-        }
-        buffer.append(entry.getValue());
-        flags &= ~entryFlag;
-      }
-    }
-
-    // We would expect to have removed all flags by this point
-    // If there's some unknown flag we've missed, format it as hexadecimal
-    if (flags != 0) {
-      if (buffer.length() > 0) {
-        buffer.append(",");
-      }
-      buffer.append("0x").append(Integer.toHexString(flags));
-    }
-
-    return buffer.toString();
-  }
-
-
-  private void cleanup(String operation) {
+  private void cleanup(String operation, StatementStoreTxInfo tx) {
     if (logger.isDebugEnabled()) {
       logger.debug("Performing cleanup from " + operation);
     }
     try {
-      synchronized(preparing) {
-        if (preparing.contains(session)) {
-          preparing.remove(session);
+      synchronized (preparing) {
+        if (preparing.contains(tx.session)) {
+          preparing.remove(tx.session);
         } else {
-          logger.debug("Already committed/rolledback in this transaction");
+          if (logger.isDebugEnabled()) {
+            logger.debug("Already committed/rolledback in this transaction");
+          }
         }
       }
     } finally {
       try {
         if (logger.isDebugEnabled()) {
-          logger.debug("Releasing session after " + operation + " " + session);
+          logger.debug("Releasing session after " + operation + " " + tx.session);
         }
-        session.release();
-        session = null;
+        tx.session.release();
+        tx.session = null;
       } catch (SimpleXAResourceException es) {
         logger.error("Attempt to release store failed", es);
       }
     }
   }
+
+  static class StatementStoreTxInfo extends TxInfo {
+    /** the underlying resolver-session to use */
+    public XAResolverSession session;
+
+    /** the underlying resources */
+    public SimpleXAResource[] resources;
+  }
 }




More information about the Mulgara-svn mailing list