You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mp...@apache.org on 2007/03/10 00:07:50 UTC

svn commit: r516597 - in /incubator/openjpa/trunk: openjpa-kernel/src/main/java/org/apache/openjpa/ee/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/ openjpa-persistence/src/main/jav...

Author: mprudhom
Date: Fri Mar  9 15:07:49 2007
New Revision: 516597

URL: http://svn.apache.org/viewvc?view=rev&rev=516597
Log:
OPENJPA-166: Added new setRollbackOnly(Throwable cause) flag so the Broker can track the reason for why the transaction would be marked for rollback, and correspondingly added reporting of the cause in LocalManagedRuntime, as well as support for WLS's corresponding support in WLSManagedRuntime.

Modified:
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/AutomaticManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/InvocationManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/JNDIManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/ManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/SunOneManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WASManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WLSManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/LocalManagedRuntime.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
    incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
    incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java
    incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/AutomaticManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/AutomaticManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/AutomaticManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/AutomaticManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -190,4 +190,26 @@
 
     public void endConfiguration() {
     }
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        // check to see if the runtime is cached
+        if (_runtime == null)
+            getTransactionManager();
+
+        if (_runtime != null)
+            _runtime.setRollbackOnly(cause);
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        // check to see if the runtime is cached
+        if (_runtime == null)
+            getTransactionManager();
+
+        if (_runtime != null)
+            return _runtime.getRollbackCause();
+
+        return null;
+    }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/InvocationManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/InvocationManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/InvocationManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/InvocationManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -76,4 +76,16 @@
 
     public void endConfiguration() {
 	}
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        getTransactionManager().getTransaction().setRollbackOnly();
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        return null;
+    }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/JNDIManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/JNDIManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/JNDIManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/JNDIManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -61,4 +61,16 @@
         }
         return _tm;
     }
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        getTransactionManager().getTransaction().setRollbackOnly();
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        return null;
+    }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/ManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/ManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/ManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/ManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -33,4 +33,24 @@
      */
     public TransactionManager getTransactionManager()
         throws Exception;
+
+    /** 
+     * Sets the rollback only flag on the current transaction. If the
+     * TransactionManager is capable of tracking the cause of the
+     * rollback-only flag, it will also pass along cause information.
+     *  
+     * @param  cause  the Throwable that caused the transaction to be
+     *                marked for rollback, or null of none is known
+     */
+    public void setRollbackOnly(Throwable cause)
+        throws Exception;
+
+    /** 
+     * Returns the Throwable that caused the current transaction to be
+     * marked for rollback, provided that any exists.
+     *
+     * @return the Throwable cause, or null if none
+     */
+    public Throwable getRollbackCause()
+        throws Exception;
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/SunOneManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/SunOneManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/SunOneManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/SunOneManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -43,4 +43,16 @@
         Object sw = _switchMeth.invoke(null, (Object[]) null);
         return (TransactionManager) _txManagerMeth.invoke(sw, (Object[]) null);
     }
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        getTransactionManager().getTransaction().setRollbackOnly();
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        return null;
+    }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WASManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WASManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WASManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WASManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -1,373 +1,373 @@
-/*
- * Copyright 2006 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.openjpa.ee;
-
-import java.io.IOException;
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.ee;
+
+import java.io.IOException;
 import java.io.InputStream;
-import java.lang.reflect.Method;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.transaction.HeuristicMixedException;
-import javax.transaction.HeuristicRollbackException;
-import javax.transaction.InvalidTransactionException;
-import javax.transaction.NotSupportedException;
-import javax.transaction.RollbackException;
-import javax.transaction.Status;
-import javax.transaction.Synchronization;
-import javax.transaction.SystemException;
-import javax.transaction.Transaction;
-import javax.transaction.xa.XAResource;
-
-import org.apache.openjpa.conf.OpenJPAConfiguration;
-import org.apache.openjpa.lib.conf.Configurable;
-import org.apache.openjpa.lib.conf.Configuration;
-import org.apache.openjpa.lib.log.Log;
-import org.apache.openjpa.lib.util.Localizer;
-import org.apache.openjpa.util.InvalidStateException;
-import org.apache.openjpa.util.NoTransactionException;
-
-import serp.bytecode.BCClass;
-import serp.bytecode.Project;
-
-/**
- * {@link ManagedRuntime} implementation that allows synchronization with a
- * WebSphere managed transaction.
- *
- * <P>
- * WebSphere Application Server does not expose the TransactionManager to an
- * application. Instead it provides a proprietary interface to register for
- * synchronization and obtain transaction ids.
- *
- * <P>
- * WASManagedRuntime provides the wrapper classes needed to interact with the
- * WAS proprietary interface and the OpenJPA kernel.
- *
- * @author Michael Dick, Kevin Sutter
- */
-public class WASManagedRuntime implements ManagedRuntime, Configurable {
-
-    private static Localizer _loc =
-        Localizer.forPackage(WASManagedRuntime.class);
-
-    private Object _extendedTransaction = null;
-    private Method _getGlobalId = null;
-    private Method _registerSync = null;
-    private OpenJPAConfiguration _conf = null;
-    private Log _log = null;
-
-    /**
-     * Gets an extendedJTATransaction from JNDI and creates a transaction
-     * wrapper
-     */
-    public javax.transaction.TransactionManager getTransactionManager()
-        throws Exception {
-        return new WASTransaction();
-    }
-
-    /**
-     * Transaction wrapper for WebSphere. WebSphere exposes a subset of the
-     * Transaction and TransactionManager interfaces to the customer. Any
-     * methods which are not exposed by WebSphere will throw an
-     * IllegalStateException to the caller.
-     *
-     * <P>
-     * Methods supported by WAS are
-     * <UL>
-     * <LI>RegisterSynchronization </LI>
-     * <LI>GetStatus</LI>
-     * </UL>
-     */
-    class WASTransaction implements javax.transaction.TransactionManager,
-        javax.transaction.Transaction {
-
-        public int getStatus() throws SystemException {
-            int rval = Status.STATUS_UNKNOWN;
-            try {
-                if (getGlobalId() != null) {
-                    rval = Status.STATUS_ACTIVE;
-                } else {
-                    rval = Status.STATUS_NO_TRANSACTION;
-                }
-            } catch (Exception e) {
-                throw new NoTransactionException(_loc
-                        .get("was-transaction-id-exception")).setCause(e);
-            }
-            return rval;
-        }
-
-        /**
-         * Provides a Transaction wrapper. The transaction wrapper mayb only be
-         * used to determine the status of the current transaction. WebSphere
-         * does not allow direct control of container transactions.
-         *
-         * @return A WebSphere transaction wrapper.
-         */
-        public Transaction getTransaction() throws SystemException {
-            return this;
-        }
-
-        /**
-         * Register for synchronization with a WebSphere managed transaction via
-         * the extendedJTATransaction interface.
-         */
-        public void registerSynchronization(Synchronization arg0)
-            throws IllegalStateException, RollbackException, SystemException {
-            if (_extendedTransaction != null) {
-                try {
-                    _registerSync.invoke(_extendedTransaction,
-                        new Object[] { new WASSynchronization(arg0) });
-                } catch (Exception e) {
-                    throw new InvalidStateException(_loc
-                        .get("was-reflection-exception")).setCause(e);
-                }
-            } else {
-                throw new InvalidStateException(_loc.get("was-lookup-error"));
-            }
-        }
-
-        /**
-         * Gets the GlobalTransaction ID of the WebSphere managed transaction.
-         * If no Global Transaction is active null will be returned.
-         *
-         * @return Null if a global transaction is not active or if an error
-         *         occurs. byte[] id if a global transaction is active.
-         */
-        private byte[] getGlobalId() {
-            byte[] rval = null;
-            try {
-                rval = (byte[]) _getGlobalId.invoke(_extendedTransaction, null);
-            } catch (Exception e) {
-                throw new InvalidStateException(_loc
-                    .get("was-reflection-exception")).setCause(e);
-            }
-            return rval;
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public void begin() throws NotSupportedException, SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "begin"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public void commit() throws HeuristicMixedException,
-            HeuristicRollbackException, IllegalStateException,
-            RollbackException, SecurityException, SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "commit"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public void resume(Transaction arg0) throws IllegalStateException,
-            InvalidTransactionException, SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "resume"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Log a
-         * trace instead of throwing an exception. Rollback may be used in
-         * some error paths, throwing another exception may result in the
-         * original exception being lost.
-         */
-        public void rollback() throws IllegalStateException, SecurityException,
-            SystemException {
-            if (_log.isTraceEnabled()) {
-                _log.trace(_loc.get("was-unsupported-op", "rollback"));
-            }
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Log a
-         * trace instead of throwing an exception. SetRollbackOnly may be used
-         * in some error paths, throwing another exception may result in the
-         * original exception being lost.
-         */
-        public void setRollbackOnly() throws IllegalStateException,
-            SystemException {
-            if (_log.isTraceEnabled()) {
-                _log.trace(_loc.get("was-unsupported-op", "setRollbackOnly"));
-            }
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public void setTransactionTimeout(int arg0) throws SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "setTransactionTimeout"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public Transaction suspend() throws SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "suspend"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public boolean delistResource(XAResource arg0, int arg1)
-            throws IllegalStateException, SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "delistResource"));
-        }
-
-        /**
-         * Unimplemented, WAS does not provide this level of control. Throws an
-         * IllegalStateException
-         */
-        public boolean enlistResource(XAResource arg0)
-            throws IllegalStateException, RollbackException, SystemException {
-            throw new InvalidStateException(_loc.get("was-unsupported-op",
-                "enlistResource"));
-        }
-    }
-
-    /**
-     * WASSynchronization wrapper. This class translates the WAS proprietary
-     * synchronization callback methods to javax.transaction.Synchronization
-     * methods.
-     *
-     * <P>
-     * This class implements the
-     * com.ibm.websphere.jtaextensions.SynchronizationCallback interface. Since
-     * SynchronizationCallback is not available at compile time we use Serp to
-     * add the interface to the class after all classes have been compiled.
-     *
-     * <P>
-     * SynchronizationCallback is expected to be available whenever this class
-     * is instantiated, therefore this class should only be used when running in
-     * WebSphere.
-     *
-     * @see org.apache.openjpa.util.WASTransformer
-     */
-    static class WASSynchronization {
-
-        Synchronization _sync = null;
-
-        WASSynchronization(Synchronization sync) {
-            _sync = sync;
-        }
-
-        /**
-         * AfterCompletion wrapper. Translates the WAS proprietary call to a
-         * javax.transaction.Synchronization call.
-         */
-        public void afterCompletion(int localTransactionId,
-            byte[] globalTransactionId, boolean committed) {
-            if (_sync != null) {
-                if (committed) {
-                    _sync.afterCompletion(Status.STATUS_COMMITTED);
-                } else {
-                    _sync.afterCompletion(Status.STATUS_UNKNOWN);
-                }
-            }
-        }
-
-        /**
-         * BeforeCompletion wrapper. Translates WAS proprietary call to a
-         * javax.transaction.Synchronization call.
-         */
-        public void beforeCompletion(int arg0, byte[] arg1) {
-            if (_sync != null) {
-                _sync.beforeCompletion();
-            }
-        }
-    }
-
-    /**
-     * Caches a copy of the configuration. The configuration is used to obtain
-     * the logger and classloader.
-     */
-    public void setConfiguration(Configuration conf) {
-        _conf = (OpenJPAConfiguration) conf;
-        _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
-    }
-
-    /**
-     * EndConfiguration stub.
-     */
-    public void endConfiguration() {
-        try {
-            Context ctx = new InitialContext();
-            try {
-                _extendedTransaction =
-                    ctx.lookup("java:comp/websphere/ExtendedJTATransaction");
-            } finally {
-                ctx.close();
-            }
-
-            ClassLoader loader = _conf.getClassResolverInstance()
-                .getClassLoader(getClass(), null);
-
-            Class extendedJTATransaction = Class.forName(
-                "com.ibm.websphere.jtaextensions.ExtendedJTATransaction", true,
-                loader);
-
-            _registerSync = extendedJTATransaction.getMethod(
-                "registerSynchronizationCallbackForCurrentTran",
-                new Class[] { Class.forName(
-                    "com.ibm.websphere.jtaextensions.SynchronizationCallback",
-                    true, loader) });
-            _getGlobalId = extendedJTATransaction.
-                getMethod("getGlobalId", null);
-        } catch (Exception e) {
-            throw new InvalidStateException(_loc
-                .get("was-reflection-exception"), e).setFatal(true);
-        }
-    }
-
-    /**
-     * StartConfiguration stub.
-     */
-    public void startConfiguration() {
-        // Nothing to do
-    }
-
-    /**
-     * Class that will be modified
-     */
-    static final String CLASS =
-        "org.apache.openjpa.ee.WASManagedRuntime$WASSynchronization";
-
-    /**
-     * Interface which will be added
-     */
-    static final String INTERFACE =
-        "com.ibm.websphere.jtaextensions.SynchronizationCallback";
-
-    public static void main(String[] args)
-        throws IOException {
-        Project project = new Project();
+import java.lang.reflect.Method;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.lib.conf.Configurable;
+import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.InvalidStateException;
+import org.apache.openjpa.util.NoTransactionException;
+
+import serp.bytecode.BCClass;
+import serp.bytecode.Project;
+
+/**
+ * {@link ManagedRuntime} implementation that allows synchronization with a
+ * WebSphere managed transaction.
+ *
+ * <P>
+ * WebSphere Application Server does not expose the TransactionManager to an
+ * application. Instead it provides a proprietary interface to register for
+ * synchronization and obtain transaction ids.
+ *
+ * <P>
+ * WASManagedRuntime provides the wrapper classes needed to interact with the
+ * WAS proprietary interface and the OpenJPA kernel.
+ *
+ * @author Michael Dick, Kevin Sutter
+ */
+public class WASManagedRuntime implements ManagedRuntime, Configurable {
+
+    private static Localizer _loc =
+        Localizer.forPackage(WASManagedRuntime.class);
+
+    private Object _extendedTransaction = null;
+    private Method _getGlobalId = null;
+    private Method _registerSync = null;
+    private OpenJPAConfiguration _conf = null;
+    private Log _log = null;
+
+    /**
+     * Gets an extendedJTATransaction from JNDI and creates a transaction
+     * wrapper
+     */
+    public javax.transaction.TransactionManager getTransactionManager()
+        throws Exception {
+        return new WASTransaction();
+    }
+
+    /**
+     * Transaction wrapper for WebSphere. WebSphere exposes a subset of the
+     * Transaction and TransactionManager interfaces to the customer. Any
+     * methods which are not exposed by WebSphere will throw an
+     * IllegalStateException to the caller.
+     *
+     * <P>
+     * Methods supported by WAS are
+     * <UL>
+     * <LI>RegisterSynchronization </LI>
+     * <LI>GetStatus</LI>
+     * </UL>
+     */
+    class WASTransaction implements javax.transaction.TransactionManager,
+        javax.transaction.Transaction {
+
+        public int getStatus() throws SystemException {
+            int rval = Status.STATUS_UNKNOWN;
+            try {
+                if (getGlobalId() != null) {
+                    rval = Status.STATUS_ACTIVE;
+                } else {
+                    rval = Status.STATUS_NO_TRANSACTION;
+                }
+            } catch (Exception e) {
+                throw new NoTransactionException(_loc
+                        .get("was-transaction-id-exception")).setCause(e);
+            }
+            return rval;
+        }
+
+        /**
+         * Provides a Transaction wrapper. The transaction wrapper mayb only be
+         * used to determine the status of the current transaction. WebSphere
+         * does not allow direct control of container transactions.
+         *
+         * @return A WebSphere transaction wrapper.
+         */
+        public Transaction getTransaction() throws SystemException {
+            return this;
+        }
+
+        /**
+         * Register for synchronization with a WebSphere managed transaction via
+         * the extendedJTATransaction interface.
+         */
+        public void registerSynchronization(Synchronization arg0)
+            throws IllegalStateException, RollbackException, SystemException {
+            if (_extendedTransaction != null) {
+                try {
+                    _registerSync.invoke(_extendedTransaction,
+                        new Object[] { new WASSynchronization(arg0) });
+                } catch (Exception e) {
+                    throw new InvalidStateException(_loc
+                        .get("was-reflection-exception")).setCause(e);
+                }
+            } else {
+                throw new InvalidStateException(_loc.get("was-lookup-error"));
+            }
+        }
+
+        /**
+         * Gets the GlobalTransaction ID of the WebSphere managed transaction.
+         * If no Global Transaction is active null will be returned.
+         *
+         * @return Null if a global transaction is not active or if an error
+         *         occurs. byte[] id if a global transaction is active.
+         */
+        private byte[] getGlobalId() {
+            byte[] rval = null;
+            try {
+                rval = (byte[]) _getGlobalId.invoke(_extendedTransaction, null);
+            } catch (Exception e) {
+                throw new InvalidStateException(_loc
+                    .get("was-reflection-exception")).setCause(e);
+            }
+            return rval;
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public void begin() throws NotSupportedException, SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "begin"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public void commit() throws HeuristicMixedException,
+            HeuristicRollbackException, IllegalStateException,
+            RollbackException, SecurityException, SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "commit"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public void resume(Transaction arg0) throws IllegalStateException,
+            InvalidTransactionException, SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "resume"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Log a
+         * trace instead of throwing an exception. Rollback may be used in
+         * some error paths, throwing another exception may result in the
+         * original exception being lost.
+         */
+        public void rollback() throws IllegalStateException, SecurityException,
+            SystemException {
+            if (_log.isTraceEnabled()) {
+                _log.trace(_loc.get("was-unsupported-op", "rollback"));
+            }
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Log a
+         * trace instead of throwing an exception. SetRollbackOnly may be used
+         * in some error paths, throwing another exception may result in the
+         * original exception being lost.
+         */
+        public void setRollbackOnly() throws IllegalStateException,
+            SystemException {
+            if (_log.isTraceEnabled()) {
+                _log.trace(_loc.get("was-unsupported-op", "setRollbackOnly"));
+            }
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public void setTransactionTimeout(int arg0) throws SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "setTransactionTimeout"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public Transaction suspend() throws SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "suspend"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public boolean delistResource(XAResource arg0, int arg1)
+            throws IllegalStateException, SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "delistResource"));
+        }
+
+        /**
+         * Unimplemented, WAS does not provide this level of control. Throws an
+         * IllegalStateException
+         */
+        public boolean enlistResource(XAResource arg0)
+            throws IllegalStateException, RollbackException, SystemException {
+            throw new InvalidStateException(_loc.get("was-unsupported-op",
+                "enlistResource"));
+        }
+    }
+
+    /**
+     * WASSynchronization wrapper. This class translates the WAS proprietary
+     * synchronization callback methods to javax.transaction.Synchronization
+     * methods.
+     *
+     * <P>
+     * This class implements the
+     * com.ibm.websphere.jtaextensions.SynchronizationCallback interface. Since
+     * SynchronizationCallback is not available at compile time we use Serp to
+     * add the interface to the class after all classes have been compiled.
+     *
+     * <P>
+     * SynchronizationCallback is expected to be available whenever this class
+     * is instantiated, therefore this class should only be used when running in
+     * WebSphere.
+     *
+     * @see org.apache.openjpa.util.WASTransformer
+     */
+    static class WASSynchronization {
+
+        Synchronization _sync = null;
+
+        WASSynchronization(Synchronization sync) {
+            _sync = sync;
+        }
+
+        /**
+         * AfterCompletion wrapper. Translates the WAS proprietary call to a
+         * javax.transaction.Synchronization call.
+         */
+        public void afterCompletion(int localTransactionId,
+            byte[] globalTransactionId, boolean committed) {
+            if (_sync != null) {
+                if (committed) {
+                    _sync.afterCompletion(Status.STATUS_COMMITTED);
+                } else {
+                    _sync.afterCompletion(Status.STATUS_UNKNOWN);
+                }
+            }
+        }
+
+        /**
+         * BeforeCompletion wrapper. Translates WAS proprietary call to a
+         * javax.transaction.Synchronization call.
+         */
+        public void beforeCompletion(int arg0, byte[] arg1) {
+            if (_sync != null) {
+                _sync.beforeCompletion();
+            }
+        }
+    }
+
+    /**
+     * Caches a copy of the configuration. The configuration is used to obtain
+     * the logger and classloader.
+     */
+    public void setConfiguration(Configuration conf) {
+        _conf = (OpenJPAConfiguration) conf;
+        _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
+    }
+
+    /**
+     * EndConfiguration stub.
+     */
+    public void endConfiguration() {
+        try {
+            Context ctx = new InitialContext();
+            try {
+                _extendedTransaction =
+                    ctx.lookup("java:comp/websphere/ExtendedJTATransaction");
+            } finally {
+                ctx.close();
+            }
+
+            ClassLoader loader = _conf.getClassResolverInstance()
+                .getClassLoader(getClass(), null);
+
+            Class extendedJTATransaction = Class.forName(
+                "com.ibm.websphere.jtaextensions.ExtendedJTATransaction", true,
+                loader);
+
+            _registerSync = extendedJTATransaction.getMethod(
+                "registerSynchronizationCallbackForCurrentTran",
+                new Class[] { Class.forName(
+                    "com.ibm.websphere.jtaextensions.SynchronizationCallback",
+                    true, loader) });
+            _getGlobalId = extendedJTATransaction.
+                getMethod("getGlobalId", null);
+        } catch (Exception e) {
+            throw new InvalidStateException(_loc
+                .get("was-reflection-exception"), e).setFatal(true);
+        }
+    }
+
+    /**
+     * StartConfiguration stub.
+     */
+    public void startConfiguration() {
+        // Nothing to do
+    }
+
+    /**
+     * Class that will be modified
+     */
+    static final String CLASS =
+        "org.apache.openjpa.ee.WASManagedRuntime$WASSynchronization";
+
+    /**
+     * Interface which will be added
+     */
+    static final String INTERFACE =
+        "com.ibm.websphere.jtaextensions.SynchronizationCallback";
+
+    public static void main(String[] args)
+        throws IOException {
+        Project project = new Project();
         
         InputStream in = WASManagedRuntime.class.getClassLoader()
             .getResourceAsStream(CLASS.replace('.', '/') + ".class");
@@ -382,7 +382,19 @@
         		}
         	}
         }
-        bcClass.declareInterface(INTERFACE);
-        bcClass.write();
-    }
-}
+        bcClass.declareInterface(INTERFACE);
+        bcClass.write();
+    }
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        getTransactionManager().getTransaction().setRollbackOnly();
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        // there is no generic support for setting the rollback cause
+        return null;
+    }
+}

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WLSManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WLSManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WLSManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/ee/WLSManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -16,6 +16,7 @@
 package org.apache.openjpa.ee;
 
 import java.lang.reflect.Method;
+import javax.transaction.Transaction;
 import javax.transaction.TransactionManager;
 
 /**
@@ -44,5 +45,35 @@
         //	getTransactionManager ();
         Object o = _txHelperMeth.invoke(null, null);
         return (TransactionManager) _txManagerMeth.invoke(o, null);
+    }
+
+    public void setRollbackOnly(Throwable cause)
+        throws Exception {
+        Transaction transaction = getTransactionManager().getTransaction();
+        try {
+            // try to use reflection to call the setRollbackOnly(Throwable)
+            // method in weblogic.transaction.Transaction
+            transaction.getClass().
+                getMethod("setRollbackOnly", new Class[] { Throwable.class }).
+                    invoke(transaction, new Object[] { cause });
+        } catch (Throwable e) {
+            // some problem occurred: fall back to the traditional call
+            transaction.setRollbackOnly();
+        }
+    }
+
+    public Throwable getRollbackCause()
+        throws Exception {
+        Transaction transaction = getTransactionManager().getTransaction();
+        try {
+            // try to use reflection to call the getRollbackReason()
+            // method in weblogic.transaction.Transaction
+            return (Throwable) transaction.getClass().
+                getMethod("getRollbackReason", new Class[0]).
+                    invoke(transaction, new Object[0]);
+        } catch (Throwable e) {
+            // some problem occurred: just return null
+            return null;
+        }
     }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java Fri Mar  9 15:07:49 2007
@@ -340,6 +340,24 @@
     public void setRollbackOnly();
 
     /**
+     * Mark the current transaction for rollback with the specified cause
+     * of the rollback.
+     *
+     * @since 0.9.7
+     */
+    public void setRollbackOnly(Throwable cause);
+
+    /** 
+     * Returns the Throwable that caused the transaction to be
+     * marked for rollback. 
+     *  
+     * @return the Throwable, or null if none was given
+     *
+     * @since 0.9.7
+     */
+    public Throwable getRollbackCause();
+
+    /**
      * Set a transactional savepoint where operations after this savepoint
      * will be rolled back.
      */

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Fri Mar  9 15:07:49 2007
@@ -682,7 +682,7 @@
                 setNestedThrowables(exceps);
         if ((mode & CALLBACK_ROLLBACK) != 0 && (_flags & FLAG_ACTIVE) != 0) {
             ce.setFatal(true);
-            setRollbackOnlyInternal();
+            setRollbackOnlyInternal(ce);
         }
         if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled())
             _log.warn(ce);
@@ -1205,12 +1205,12 @@
         } catch (OpenJPAException ke) {
             // if we already started the transaction, don't let it commit
             if ((_flags & FLAG_ACTIVE) != 0)
-                setRollbackOnlyInternal();
+                setRollbackOnlyInternal(ke);
             throw ke.setFatal(true);
         } catch (RuntimeException re) {
             // if we already started the transaction, don't let it commit
             if ((_flags & FLAG_ACTIVE) != 0)
-                setRollbackOnlyInternal();
+                setRollbackOnlyInternal(re);
             throw new StoreException(re).setFatal(true);
         }
 
@@ -1408,11 +1408,38 @@
         }
     }
 
+    public Throwable getRollbackCause() {
+        beginOperation(true);
+        try {
+            if ((_flags & FLAG_ACTIVE) == 0)
+                return null;
+
+            javax.transaction.Transaction trans =
+                _runtime.getTransactionManager().getTransaction();
+            if (trans == null)
+                return null;
+            if (trans.getStatus() == Status.STATUS_MARKED_ROLLBACK)
+                return _runtime.getRollbackCause();
+
+            return null;
+        } catch (OpenJPAException ke) {
+            throw ke;
+        } catch (Exception e) {
+            throw new GeneralException(e);
+        } finally {
+            endOperation();
+        }
+    }
+
     public void setRollbackOnly() {
+        setRollbackOnly(new UserException());
+    }
+
+    public void setRollbackOnly(Throwable cause) {
         beginOperation(true);
         try {
             assertTransactionOperation();
-            setRollbackOnlyInternal();
+            setRollbackOnlyInternal(cause);
         } finally {
             endOperation();
         }
@@ -1421,13 +1448,13 @@
     /**
      * Mark the current transaction as rollback-only.
      */
-    private void setRollbackOnlyInternal() {
+    private void setRollbackOnlyInternal(Throwable cause) {
         try {
             javax.transaction.Transaction trans =
                 _runtime.getTransactionManager().getTransaction();
             if (trans == null)
                 throw new InvalidStateException(_loc.get("null-trans"));
-            trans.setRollbackOnly();
+            _runtime.setRollbackOnly(cause);
         } catch (OpenJPAException ke) {
             throw ke;
         } catch (Exception e) {
@@ -1613,11 +1640,11 @@
                 _flags |= FLAG_FLUSHED;
             } catch (OpenJPAException ke) {
                 // rollback on flush error; objects may be in inconsistent state
-                setRollbackOnly();
+                setRollbackOnly(ke);
                 throw ke.setFatal(true);
             } catch (RuntimeException re) {
                 // rollback on flush error; objects may be in inconsistent state
-                setRollbackOnly();
+                setRollbackOnly(re);
                 throw new StoreException(re).setFatal(true);
             }
         }
@@ -3145,7 +3172,7 @@
             try {
                 return new AttachManager(this, copyNew, call).attach(obj);
             } catch (OptimisticException oe) {
-                setRollbackOnly();
+                setRollbackOnly(oe);
                 throw oe.setFatal(true);
             } catch (OpenJPAException ke) {
                 throw ke;
@@ -3172,7 +3199,7 @@
             try {
                 return new AttachManager(this, copyNew, call).attachAll(objs);
             } catch (OptimisticException oe) {
-                setRollbackOnly();
+                setRollbackOnly(oe);
                 throw oe.setFatal(true);
             } catch (OpenJPAException ke) {
                 throw ke;
@@ -3609,7 +3636,7 @@
             // transaction to complete before we have a chance to set the
             // rollback only flag
             if ((_flags & FLAG_STORE_FLUSHING) != 0)
-                setRollbackOnlyInternal();
+                setRollbackOnlyInternal(new UserException());
             return _store.cancelAll();
         } catch (OpenJPAException ke) {
             throw ke;

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java Fri Mar  9 15:07:49 2007
@@ -906,6 +906,22 @@
         }
     }
 
+    public void setRollbackOnly(Throwable cause) {
+        try {
+            _broker.setRollbackOnly(cause);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public Throwable getRollbackCause() {
+        try {
+            return _broker.getRollbackCause();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
     public boolean getRollbackOnly() {
         try {
             return _broker.getRollbackOnly();

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/LocalManagedRuntime.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/LocalManagedRuntime.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/LocalManagedRuntime.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/LocalManagedRuntime.java Fri Mar  9 15:07:49 2007
@@ -28,6 +28,7 @@
 import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.InvalidStateException;
 import org.apache.openjpa.util.StoreException;
+import org.apache.openjpa.util.UserException;
 
 /**
  * Uses a local implementation of the {@link TransactionManager} interface.
@@ -45,7 +46,7 @@
     private Synchronization _broker = null;
     private Synchronization _factorySync = null;
     private boolean _active = false;
-    private boolean _rollbackOnly = false;
+    private Throwable _rollbackOnly = null;
 
     /**
      * Constructor. Provide broker that will be requesting managed
@@ -71,20 +72,20 @@
 
         // try to invoke before completion in preparation for commit
         RuntimeException err = null;
-        if (!_rollbackOnly) {
+        if (_rollbackOnly == null) {
             try {
                 _broker.beforeCompletion();
                 if (_factorySync != null)
                     _factorySync.beforeCompletion();
             } catch (RuntimeException re) {
-                _rollbackOnly = true;
+                _rollbackOnly = re;
                 err = re;
             }
         } else // previously marked rollback only
             err = new StoreException(_loc.get("marked-rollback")).
-                setFatal(true);
+                setCause(_rollbackOnly).setFatal(true);
 
-        if (!_rollbackOnly) {
+        if (_rollbackOnly == null) {
             try {
                 _broker.afterCompletion(Status.STATUS_COMMITTED);
                 notifyAfterCompletion(Status.STATUS_COMMITTED);
@@ -145,17 +146,25 @@
             if (_factorySync != null)
                 _factorySync.afterCompletion(status);
         } finally {
-            _rollbackOnly = false;
+            _rollbackOnly = null;
             _factorySync = null;
         }
     }
 
     public synchronized void setRollbackOnly() {
-        _rollbackOnly = true;
+        setRollbackOnly(new UserException());
+    }
+
+    public void setRollbackOnly(Throwable cause) {
+        _rollbackOnly = cause;
+    }
+
+    public Throwable getRollbackCause() {
+        return _rollbackOnly;
     }
 
     public synchronized int getStatus() {
-        if (_rollbackOnly)
+        if (_rollbackOnly != null)
             return Status.STATUS_MARKED_ROLLBACK;
         if (_active)
             return Status.STATUS_ACTIVE;

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties Fri Mar  9 15:07:49 2007
@@ -59,7 +59,8 @@
 	passed to lookup of type "{1}" is not a OpenJPA id instance.
 null-oids: Some of the object ids passed to getObjectsById were null.
 marked-rollback: The transaction cannot be committed, because it was already \
-	marked for rollback only.  The transaction will be rolled back instead.
+	marked for rollback only.  The transaction will be rolled back instead. \
+    The cause of the rollback-only status is reported in the embedded stack.
 refresh-flushed: You cannot refresh an instance that has been flushed to the \
 	data store.
 pc-loader-different: Attempt to cast instance "{0}" to PersistenceCapable failed. \

Modified: incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java Fri Mar  9 15:07:49 2007
@@ -430,6 +430,14 @@
         _broker.rollbackAndResume();
     }
 
+    public Throwable getRollbackCause() {
+        if (!isActive())
+            throw new IllegalStateException(_loc.get("no-transaction")
+                .getMessage());
+
+        return _broker.getRollbackCause();
+    }
+
     public boolean getRollbackOnly() {
         if (!isActive())
             throw new IllegalStateException(_loc.get("no-transaction")
@@ -440,6 +448,10 @@
 
     public void setRollbackOnly() {
         _broker.setRollbackOnly();
+    }
+
+    public void setRollbackOnly(Throwable cause) {
+        _broker.setRollbackOnly(cause);
     }
 
     public void setSavepoint(String name) {

Modified: incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java (original)
+++ incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java Fri Mar  9 15:07:49 2007
@@ -408,6 +408,24 @@
     public void setRollbackOnly();
 
     /**
+     * Mark the current transaction for rollback with the specified cause
+     * of the rollback.
+     *
+     * @since 0.9.7
+     */
+    public void setRollbackOnly(Throwable cause);
+
+    /** 
+     * Returns the Throwable that caused the transaction to be
+     * marked for rollback. 
+     *  
+     * @return the Throwable, or null if none was given
+     *
+     * @since 0.9.7
+     */
+    public Throwable getRollbackCause();
+
+    /**
      * Return whether the current transaction has been marked for rollback.
      */
     public boolean getRollbackOnly();

Modified: incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java?view=diff&rev=516597&r1=516596&r2=516597
==============================================================================
--- incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java (original)
+++ incubator/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java Fri Mar  9 15:07:49 2007
@@ -61,7 +61,7 @@
                     try {
                         throwing = true;
                         if (em.isOpen() && em.isActive())
-                            em.setRollbackOnly();
+                            em.setRollbackOnly(re);
                     } finally {
                         // handle re-entrancy
                         throwing = false;