001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.enterprise.bo.impl;
016:
017: import java.util.HashMap;
018: import java.util.Iterator;
019:
020: import javax.naming.Context;
021: import javax.naming.InitialContext;
022: import javax.naming.NamingException;
023: import javax.transaction.NotSupportedException;
024: import javax.transaction.RollbackException;
025: import javax.transaction.Status;
026: import javax.transaction.Synchronization;
027: import javax.transaction.SystemException;
028: import javax.transaction.Transaction;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import com.metaboss.enterprise.bo.BOException;
034: import com.metaboss.enterprise.bo.BONamingAndDirectoryServiceInvocationException;
035: import com.metaboss.enterprise.bo.BOObjectDomainAlreadyInTransactionException;
036: import com.metaboss.enterprise.bo.BOTransactionRequiredException;
037: import com.metaboss.enterprise.bo.BOTransactionRollbackException;
038: import com.metaboss.enterprise.spi.JTAImplementationProvider;
039: import com.metaboss.enterprise.transaction.ExtendedTransactionStatus;
040: import com.metaboss.util.ThreadSafeStorage;
041:
042: /** The controller of the Domains and entities put in transaction */
043: public class InternalTransactionController implements Synchronization {
044: // Static logging instance
045: private static final Log sLogger = LogFactory
046: .getLog(InternalTransactionController.class);
047:
048: private static Object cJTAImplementationProviderCreationSemaphore = new Object();
049: private static JTAImplementationProvider sJTAImplementationProvider = null;
050: private static ThreadSafeStorage sTransactionControllerStorage = new ThreadSafeStorage();
051: private HashMap mDomainsInTransaction = new HashMap();
052: private Transaction mTransaction = null;
053: private ExtendedTransactionStatus mExtendedTransactionStatus = null;
054:
055: protected static InternalTransactionController getInstance()
056: throws BOException {
057: // Initialise transaction implementation provider if necessary
058: if (sJTAImplementationProvider == null) {
059: synchronized (cJTAImplementationProviderCreationSemaphore) {
060: if (sJTAImplementationProvider == null) {
061: try {
062: // Hook up to the JDBCConnectionProvider
063: Context ctx = new InitialContext();
064: sJTAImplementationProvider = (JTAImplementationProvider) ctx
065: .lookup(JTAImplementationProvider.COMPONENT_URL);
066: } catch (NamingException e) {
067: throw new BONamingAndDirectoryServiceInvocationException(
068: "Unable to locate JTAImplementationProvider",
069: e);
070: }
071: }
072: }
073: }
074: // Get existing or create new internal transaction controller
075: InternalTransactionController lTxController = (InternalTransactionController) sTransactionControllerStorage
076: .get();
077: if (lTxController == null) {
078: sLogger.debug("Creating new instance for the thread "
079: + Thread.currentThread().getName());
080: sTransactionControllerStorage
081: .put(lTxController = new InternalTransactionController());
082: }
083: return lTxController;
084: }
085:
086: private InternalTransactionController() throws BOException {
087: try {
088: mTransaction = sJTAImplementationProvider
089: .getTransactionManager().getTransaction();
090: if (mTransaction == null
091: || mTransaction.getStatus() != Status.STATUS_ACTIVE)
092: throw new BOTransactionRequiredException();
093: mTransaction.registerSynchronization(this );
094: mExtendedTransactionStatus = sJTAImplementationProvider
095: .getExtendedTransactionStatus();
096: } catch (SystemException e) {
097: throw new BOTransactionRequiredException(e);
098: } catch (RollbackException e) {
099: throw new BOTransactionRollbackException(e);
100: }
101: }
102:
103: /** Registers domain object for editing before commit */
104: public void registerDomain(BOObjectDomainImpl pBODomainImpl)
105: throws BOException {
106: if (mDomainsInTransaction.containsKey(pBODomainImpl
107: .getDomainType()))
108: throw new BOObjectDomainAlreadyInTransactionException();
109: mDomainsInTransaction.put(pBODomainImpl.getDomainType(),
110: pBODomainImpl);
111: }
112:
113: /** javax.transaction.Synchronization method */
114: public void afterCompletion(int pStatus) {
115: sLogger
116: .debug("Commencing afterCompletion() transaction synchronisation operation.");
117: sLogger.debug("Transaction status is "
118: + com.metaboss.enterprise.transaction.Utils
119: .getTransactionStatusName(pStatus));
120: sLogger.debug("Found "
121: + mDomainsInTransaction.size()
122: + " domains to close transaction in. Changes will be "
123: + ((pStatus == Status.STATUS_COMMITTED) ? "committed"
124: : "rolled back"));
125: try {
126: // Now deal with domains
127: for (Iterator lDomainsIterator = mDomainsInTransaction
128: .values().iterator(); lDomainsIterator.hasNext();) {
129: BOObjectDomainImpl lBODomainImpl = (BOObjectDomainImpl) lDomainsIterator
130: .next();
131: if (pStatus == Status.STATUS_COMMITTED)
132: lBODomainImpl.doCommit();
133: else
134: lBODomainImpl.doRollback();
135: }
136: } catch (Throwable t) {
137: sLogger
138: .error(
139: "Caught error while processing transaction completion",
140: t);
141: }
142: sTransactionControllerStorage.remove(this );
143: }
144:
145: /** javax.transaction.Synchronization method */
146: public void beforeCompletion() {
147: try {
148: int lStatus = mTransaction.getStatus();
149: if (sLogger.isDebugEnabled()) {
150: sLogger
151: .debug("Commencing beforeCompletion() transaction synchronisation operation.");
152: sLogger.debug("Transaction status is "
153: + com.metaboss.enterprise.transaction.Utils
154: .getTransactionStatusName(lStatus));
155: }
156: // Only save changes if transaction is still
157: if (lStatus == Status.STATUS_ACTIVE) {
158: if (sLogger.isDebugEnabled()) {
159: sLogger.debug("Found "
160: + mDomainsInTransaction.size()
161: + " domains to save changes in.");
162: }
163: // Now deal with domains in the loop
164: // Any exception should lead to the transaction rollback
165: // so we will catch exceptions on the top level
166: // Once transaction becomes anything but active - we will simply exit
167: for (Iterator lDomainsIterator = mDomainsInTransaction
168: .values().iterator(); lDomainsIterator
169: .hasNext();) {
170: BOObjectDomainImpl lBODomainImpl = (BOObjectDomainImpl) lDomainsIterator
171: .next();
172: lBODomainImpl.saveChanges();
173: if ((lStatus = mTransaction.getStatus()) != Status.STATUS_ACTIVE) {
174: if (sLogger.isDebugEnabled()) {
175: sLogger
176: .debug("Transaction has changed state while saving changes to the '"
177: + lBODomainImpl
178: .getDomainType()
179: + "' Domain. New transaction status is "
180: + com.metaboss.enterprise.transaction.Utils
181: .getTransactionStatusName(lStatus));
182: sLogger
183: .debug("New transaction status is "
184: + com.metaboss.enterprise.transaction.Utils
185: .getTransactionStatusName(lStatus));
186: sLogger
187: .debug("This is not expected and the beforeCompletion() transaction synchronisation operation will abort further saving of changes.");
188: }
189: return;
190: }
191: }
192: } else {
193: if (sLogger.isDebugEnabled()) {
194: sLogger
195: .debug("This is not expected and the beforeCompletion() transaction synchronisation operation will not attempt saving of changes.");
196: }
197: return;
198: }
199: } catch (Throwable t) {
200: try {
201: sLogger
202: .debug(
203: "Setting transaction Rollback Only due to exception",
204: t);
205: mTransaction.setRollbackOnly();
206: mExtendedTransactionStatus.setRollbackCause(t);
207: } catch (SystemException e) {
208: // Ignore it here
209: } catch (NotSupportedException e) {
210: // Ignore it here
211: }
212: }
213: }
214: }
|