/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.kernel; import javax.transaction.NotSupportedException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.xa.XAResource; import org.apache.openjpa.ee.AbstractManagedRuntime; import org.apache.openjpa.ee.ManagedRuntime; import org.apache.openjpa.lib.util.Localizer; 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. * This manager is valid only for a single {@link Broker}. * It duplicates non-managed transaction control. * * @author Abe White */ class LocalManagedRuntime extends AbstractManagedRuntime implements ManagedRuntime, TransactionManager, Transaction { private static final Localizer _loc = Localizer.forPackage (LocalManagedRuntime.class); private Synchronization _broker = null; private Synchronization _factorySync = null; private boolean _active = false; private Throwable _rollbackOnly = null; /** * Constructor. Provide broker that will be requesting managed * transaction info. */ public LocalManagedRuntime(Broker broker) { _broker = broker; } public TransactionManager getTransactionManager() { return this; } public synchronized void begin() { if (_active) throw new InvalidStateException(_loc.get("active")); _active = true; } public synchronized void commit() { if (!_active) throw new InvalidStateException(_loc.get("not-active")); // try to invoke before completion in preparation for commit RuntimeException err = null; if (_rollbackOnly == null) { try { _broker.beforeCompletion(); if (_factorySync != null) _factorySync.beforeCompletion(); } catch (RuntimeException re) { _rollbackOnly = re; err = re; } } else // previously marked rollback only err = new StoreException(_loc.get("marked-rollback")). setCause(_rollbackOnly).setFatal(true); if (_rollbackOnly == null) { try { _broker.afterCompletion(Status.STATUS_COMMITTED); notifyAfterCompletion(Status.STATUS_COMMITTED); } catch (RuntimeException re) { if (err == null) err = re; } } // if we haven't managed to commit, rollback if (_active) { try { rollback(); } catch (RuntimeException re) { if (err == null) err = re; } } // throw the first exception we encountered, if any if (err != null) throw err; } public synchronized void rollback() { if (!_active) throw new InvalidStateException(_loc.get("not-active")); // rollback broker RuntimeException err = null; try { _broker.afterCompletion(Status.STATUS_ROLLEDBACK); } catch (RuntimeException re) { err = re; } // rollback synch, even if broker throws exception try { notifyAfterCompletion(Status.STATUS_ROLLEDBACK); } catch (RuntimeException re) { if (err == null) err = re; } if (err != null) throw err; } /** * Notifies the factory sync that the transaction has ended with * the given status. Clears all transaction state regardless * of any exceptions during the callback. */ private void notifyAfterCompletion(int status) { _active = false; try { if (_factorySync != null) _factorySync.afterCompletion(status); } finally { _rollbackOnly = null; _factorySync = null; } } public synchronized void setRollbackOnly() { setRollbackOnly(new UserException()); } public void setRollbackOnly(Throwable cause) { _rollbackOnly = cause; } public Throwable getRollbackCause() { return _rollbackOnly; } public synchronized int getStatus() { if (_rollbackOnly != null) return Status.STATUS_MARKED_ROLLBACK; if (_active) return Status.STATUS_ACTIVE; return Status.STATUS_NO_TRANSACTION; } public Transaction getTransaction() { return this; } public void resume(Transaction tobj) throws SystemException { throw new SystemException(NotSupportedException.class.getName()); } public void setTransactionTimeout(int sec) throws SystemException { throw new SystemException(NotSupportedException.class.getName()); } public Transaction suspend() throws SystemException { throw new SystemException(NotSupportedException.class.getName()); } public boolean delistResource(XAResource xaRes, int flag) throws SystemException { throw new SystemException(NotSupportedException.class.getName()); } public boolean enlistResource(XAResource xaRes) throws SystemException { throw new SystemException(NotSupportedException.class.getName()); } public synchronized void registerSynchronization(Synchronization sync) { if (sync == _broker) return; if (_factorySync != null) throw new InternalException(); _factorySync = sync; } }