All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.jta.GlobalTransaction Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal.jta;

/**
 * 

* GlobalTransaction is the JTA concept of a Global Transaction. *

* * @author Mitul Bid * * @since 4.0 */ import com.gemstone.gemfire.i18n.LogWriterI18n; import com.gemstone.gemfire.internal.i18n.LocalizedStrings; import java.util.*; import javax.transaction.xa.*; import javax.transaction.*; import com.gemstone.gemfire.SystemFailure; import com.gemstone.gemfire.distributed.DistributedSystemDisconnectedException; import com.gemstone.gemfire.distributed.internal.DM; import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; public class GlobalTransaction { public static boolean DISABLE_TRANSACTION_TIMEOUT_SETTING = false; /** * GTid is a byte array identifying every instance of a global transaction * uniquely */ private final byte[] GTid; /** * An instance of the XidImpl class which implements Xid */ private final Xid xid; /** * Status represents the state the Global Transaction is in */ private int status = Status.STATUS_UNKNOWN; /** * A set of XAResources associated with the Global Transaction */ private final Map resourceMap = Collections.synchronizedMap(new HashMap()); /** * List of local Transactions participating in a Global Transaction. */ private final List transactions = Collections.synchronizedList(new ArrayList()); /** * A counter to uniquely generate the GTid */ private static long mCounter = 1; /** * A timer Task for Transaction TimeOut */ private boolean timedOut = false; /** * expirationTime for the Transaction */ private volatile long expirationTime; /* * to enable VERBOSE = true pass System parameter jta.VERBOSE = true while * running the test. */ private static boolean VERBOSE = Boolean.getBoolean("jta.VERBOSE"); /** * Construct a new Global Transaction. Generates the GTid and also the xid */ public GlobalTransaction() throws SystemException { try { GTid = generateGTid(); xid = XidImpl.createXid(GTid); } catch (Exception e) { LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (writer.severeEnabled()) writer.severe(LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_CONSTRUCTOR_ERROR_WHILE_TRYING_TO_CREATE_XID_DUE_TO_0, e, e); String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_CONSTRUCTOR_ERROR_WHILE_TRYING_TO_CREATE_XID_DUE_TO_0.toLocalizedString(new Object[] {e}); final SystemException ex = new SystemException(exception); ex.initCause(e); throw ex; } } /** * Add a transaction to the list of transactions participating in this global * Transaction The list of transactions is being maintained so that we can * remove the local transaction to global transaction entries from the map * being maintained by the Transaction Manager * * @param txn Transaction instance which is participating in this Global * Transaction */ public void addTransaction(Transaction txn) throws SystemException { if (txn == null) { String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ADDTRANSACTION_CANNOT_ADD_A_NULL_TRANSACTION.toLocalizedString(); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); throw new SystemException(exception); } transactions.add(txn); } /** * Delists the XAResources associated with the Global Transaction and * Completes the Global transaction associated with the current thread. If any * exception is encountered, rollback is called on the current transaction. * @.concurrency Some paths invoke this method after taking a lock on "this" while * other paths invoke this method without taking a lock on "this". Since both types of path do * act on the resourceMap collection, it is being protected by a lock on resourceMap too. * * @throws RollbackException - * Thrown to indicate that the transaction has been rolled back * rather than committed. * @throws HeuristicMixedException - * Thrown to indicate that a heuristic decision was made and that * some relevant updates have been committed while others have been * rolled back. * @throws HeuristicRollbackException - * Thrown to indicate that a heuristic decision was made and that * all relevant updates have been rolled back. * @throws java.lang.SecurityException - * Thrown to indicate that the thread is not allowed to commit the * transaction. * @throws java.lang.IllegalStateException - * Thrown if the current thread is not associated with a * transaction. * @throws SystemException - * Thrown if the transaction manager encounters an unexpected error * condition. * * @see javax.transaction.TransactionManager#commit() */ //Asif : Changed the return type to int indicating the nature of Exception // encountered during commit public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException { LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); try { XAResource xar = null; XAResource xar1 = null; int loop = 0; Boolean isActive = Boolean.FALSE; synchronized (this.resourceMap) { Map.Entry entry; Iterator iterator = resourceMap.entrySet().iterator(); while (iterator.hasNext()) { try { entry = (Map.Entry)iterator.next(); xar = (XAResource)entry.getKey(); isActive = (Boolean)entry.getValue(); if (loop == 0) xar1 = xar; loop++; if (isActive.booleanValue()) { // delistResource(xar, XAResource.TMSUCCESS); xar.end(xid, XAResource.TMSUCCESS); entry.setValue(Boolean.FALSE); } } catch (Exception e) { if (VERBOSE) writer.info( LocalizedStrings.ONE_ARG, "GlobalTransaction::commit:Exception in delisting XAResource", e); } } } if (xar1 != null) xar1.commit(xid, true); status = Status.STATUS_COMMITTED; if (VERBOSE) writer .fine("GlobalTransaction::commit:Transaction committed successfully"); } catch (Exception e) { status = Status.STATUS_ROLLING_BACK; try { rollback(); } catch (Throwable t) { Error err; if (t instanceof Error && SystemFailure.isJVMFailureError( err = (Error)t)) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } // Whenever you catch Error or Throwable, you must also // check for fatal JVM error (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); // we will throw an error later, make sure that the synchronizations rollback status = Status.STATUS_ROLLEDBACK; String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_COMMIT_ERROR_IN_COMMITTING_BUT_TRANSACTION_COULD_NOT_BE_ROLLED_BACK_DUE_TO_EXCEPTION_0.toLocalizedString(t); if (VERBOSE) writer.fine(exception, t); final SystemException sysEx = new SystemException(exception); sysEx.initCause(t); throw sysEx; } String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_COMMIT_ERROR_IN_COMMITTING_THE_TRANSACTION_TRANSACTION_ROLLED_BACK_EXCEPTION_0_1.toLocalizedString(new Object[] {e, " " + (e instanceof XAException ? ("Error Code =" + ((XAException)e).errorCode) : "")}); if (VERBOSE) writer.fine(exception, e); RollbackException rbEx = new RollbackException(exception); rbEx.initCause(e); throw rbEx; } finally { //Map globalTransactions = tm.getGlobalTransactionMap(); TransactionManagerImpl.getTransactionManager().cleanGlobalTransactionMap( transactions); //Asif : Clear the list of transactions transactions.clear(); } } /** * Delists the XAResources associated with the Global Transaction and Roll * back the transaction associated with the current thread. * * @throws java.lang.SecurityException - * Thrown to indicate that the thread is not allowed to roll back * the transaction. * @throws java.lang.IllegalStateException - * Thrown if the current thread is not associated with a * transaction. * @throws SystemException - * Thrown if the transaction manager encounters an unexpected error * condition. * @.concurrency Some paths invoke this method after taking a lock on "this" while * other paths invoke this method without taking a lock on "this". Since both types of path do * act on the resourceMap collection, it is being protected by a lock on resourceMap too. * * @see javax.transaction.TransactionManager#rollback() */ public void rollback() throws IllegalStateException, SystemException { LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); try { XAResource xar = null; XAResource xar1 = null; int loop = 0; synchronized (this.resourceMap) { Iterator iterator = resourceMap.entrySet().iterator(); Boolean isActive = Boolean.FALSE; Map.Entry entry; while (iterator.hasNext()) { try { entry = (Map.Entry)iterator.next(); xar = (XAResource)entry.getKey(); isActive = (Boolean)entry.getValue(); if (loop == 0) { xar1 = xar; } loop++; if (isActive.booleanValue()) { // delistResource(xar, XAResource.TMSUCCESS); xar.end(xid, XAResource.TMSUCCESS); entry.setValue(Boolean.FALSE); } } catch (Exception e) { if (VERBOSE) writer.info( LocalizedStrings.ONE_ARG, "GlobalTransaction::rollback:Exception in delisting XAResource", e); } } } if (xar1 != null) xar1.rollback(xid); status = Status.STATUS_ROLLEDBACK; if (VERBOSE) writer.fine("Transaction rolled back successfully"); } catch (Exception e) { // we will throw an error later, make sure that the synchronizations rollback status = Status.STATUS_ROLLEDBACK; String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ROLLBACK_ROLLBACK_NOT_SUCCESSFUL_DUE_TO_EXCEPTION_0_1.toLocalizedString(new Object[] {e, " " + (e instanceof XAException ? ("Error Code =" + ((XAException)e).errorCode) : "")}); if (VERBOSE) writer.fine(exception); final SystemException sysEx = new SystemException(exception); sysEx.initCause(e); throw sysEx; } finally { // Map globalTransactions = tm.getGlobalTransactionMap(); TransactionManagerImpl.getTransactionManager().cleanGlobalTransactionMap( transactions); // Asif : Clear the list of transactions transactions.clear(); } } /** * Mark the state of the Global Transaction so that it can be rolled back */ public void setRollbackOnly() throws IllegalStateException, SystemException { setStatus(Status.STATUS_MARKED_ROLLBACK); } /** * Get the transaction state of the Global Transaction */ public int getStatus() throws SystemException { return status; } /** * Enlist the specified XAResource with this transaction. Currently only one * Resource Manager is being supported. enlistResource checks if there is no * XAResource, then enlists the current XAResource. For subsequent * XAResources, it checks if is the same Resource Manager. If it is, then the * XAResources are addded, else an exception is thrown * @.concurrency The order of acquiring lock will be lock on "this" followed * by lock on resourceMap. It is possible that in some functions of this * class both the locks are not needed , but if the two are acquired then the realitive * order will always be"this" followed by resourceMap. * * @param xaRes XAResource to be enlisted * @return true, if resource was enlisted successfully, otherwise false. * @throws SystemException - Thrown if the transaction manager encounters an * unexpected error condition. * @throws IllegalStateException - Thrown if the transaction in the target * object is in the prepared state or the transaction is inactive. * @throws RollbackException - Thrown to indicate that the transaction has * been marked for rollback only. * * @see javax.transaction.Transaction#enlistResource(javax.transaction.xa.XAResource) */ public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException { XAResource xar = null; try { synchronized (this) { if (status == Status.STATUS_MARKED_ROLLBACK) { String exception = "GlobalTransaction::enlistResource::Cannot enlist resource as the transaction has been marked for rollback"; LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); throw new RollbackException(exception); } else if (status != Status.STATUS_ACTIVE) { String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ENLISTRESOURCE_CANNOT_ENLIST_A_RESOURCE_TO_A_TRANSACTION_WHICH_IS_NOT_ACTIVE.toLocalizedString(); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); throw new IllegalStateException(exception); } if (resourceMap.isEmpty()) { xaRes.start(xid, XAResource.TMNOFLAGS); int delay = (int) ((expirationTime - System.currentTimeMillis()) / 1000); try { if(!DISABLE_TRANSACTION_TIMEOUT_SETTING) { xaRes.setTransactionTimeout(delay); } } catch (XAException xe) { String exception = LocalizedStrings.GlobalTransaction_GLOBALTRANSACTION_ENLISTRESOURCE_EXCEPTION_OCCURED_IN_TRYING_TO_SET_XARESOURCE_TIMEOUT_DUE_TO_0_ERROR_CODE_1.toLocalizedString(new Object[] {xe, Integer.valueOf(xe.errorCode)}); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); final SystemException ex = new SystemException(exception); ex.initCause(xe); throw ex; } resourceMap.put(xaRes, Boolean.TRUE); } else { synchronized (this.resourceMap) { Iterator iterator = resourceMap.keySet().iterator(); xar = (XAResource) iterator.next(); } if (!xar.isSameRM(xaRes)) { LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (writer.severeEnabled()) writer.severe( LocalizedStrings.GlobalTransaction_GLOBALTRANSACTIONENLISTRESOURCEONLY_ONE_RESOUCE_MANAGER_SUPPORTED); throw new SystemException(LocalizedStrings.GlobalTransaction_GLOBALTRANSACTIONENLISTRESOURCEONLY_ONE_RESOUCE_MANAGER_SUPPORTED.toLocalizedString()); } else { xaRes.start(xid, XAResource.TMJOIN); resourceMap.put(xaRes, Boolean.TRUE); } } } } catch (Exception e) { String addon = (e instanceof XAException ? ("Error Code =" + ((XAException) e).errorCode) : ""); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(LocalizedStrings.GLOBALTRANSACTION__ENLISTRESOURCE__ERROR_WHILE_ENLISTING_XARESOURCE_0_1.toLocalizedString(new Object[] {e, addon}), e); final SystemException sysEx = new SystemException(LocalizedStrings .GLOBALTRANSACTION__ENLISTRESOURCE__ERROR_WHILE_ENLISTING_XARESOURCE_0_1 .toLocalizedString(new Object[] {e, addon})); sysEx.initCause(e); throw sysEx; } return true; } /** * Disassociate the XAResource specified from this transaction. * * In the current implementation this call will never be made by the * application server. The delisting is happening at the time of * commit/rollback * * @param xaRes XAResource to be delisted * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL. * @return true, if resource was delisted successfully, otherwise false. * @throws SystemException Thrown if the transaction manager encounters an * unexpected error condition. * @throws IllegalStateException Thrown if the transaction in the target * object is not active. * * @see javax.transaction.Transaction#delistResource(javax.transaction.xa.XAResource, * int) */ public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException { try { if (resourceMap.containsKey(xaRes)) { Boolean isActive = (Boolean) resourceMap.get(xaRes); if (isActive.booleanValue()) { xaRes.end(xid, flag); resourceMap.put(xaRes, Boolean.FALSE); } } } catch (Exception e) { String exception = LocalizedStrings.GlobalTransaction_ERROR_WHILE_DELISTING_XARESOURCE_0_1.toLocalizedString(new Object[] {e, " " + (e instanceof XAException ? ("Error Code =" + ((XAException) e).errorCode) : "")}); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception, e); final SystemException sysEx = new SystemException(exception); sysEx.initCause(e); throw sysEx; } return true; } /** * Set the transaction state of the Global Transaction * * @param new_status Status (int) */ public void setStatus(int new_status) { status = new_status; } /** * suspends the current transaction by deactivating the XAResource (delist) */ public void suspend() throws SystemException { XAResource xar = null; synchronized (this.resourceMap) { Iterator iterator = resourceMap.entrySet().iterator(); Map.Entry entry; Boolean isActive = Boolean.FALSE; while (iterator.hasNext()) { entry = (Map.Entry)iterator.next(); xar = (XAResource)entry.getKey(); isActive = (Boolean)entry.getValue(); if (isActive.booleanValue()) { try { // delistResource(xar, XAResource.TMSUCCESS); xar.end(xid, XAResource.TMSUSPEND); entry.setValue(Boolean.FALSE); } catch (Exception e) { String exception = LocalizedStrings.GlobalTransaction_ERROR_WHILE_DELISTING_XARESOURCE_0_1.toLocalizedString(new Object[] {e, " " + (e instanceof XAException ? ("Error Code =" + ((XAException) e).errorCode) : "")}); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); final SystemException ex = new SystemException(exception); ex.initCause(e); throw ex; } } } } } /** * resume the current transaction by activating all the XAResources associated * with the current transaction */ public void resume() throws SystemException { XAResource xar = null; synchronized (this.resourceMap) { Iterator iterator = resourceMap.entrySet().iterator(); Map.Entry entry; Boolean isActive = Boolean.FALSE; while (iterator.hasNext()) { entry = (Map.Entry)iterator.next(); xar = (XAResource)entry.getKey(); isActive = (Boolean)entry.getValue(); if (!isActive.booleanValue()) try { xar.start(xid, XAResource.TMRESUME); entry.setValue(Boolean.TRUE); } catch (Exception e) { String exception = LocalizedStrings.GlobalTransaction_GLOBATRANSACTION_RESUME_RESUME_NOT_SUCCESFUL_DUE_TO_0.toLocalizedString(e); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception, e); final SystemException ex = new SystemException(exception); ex.initCause(e); throw ex; } } } } /** * String for current distributed system * * @see #IdsForId * @guarded.By {@link #DmidMutex} */ private static String DMid = null; /** * Distributed system for given string * @see #DMid * @guarded.By {@link #DmidMutex} */ private static InternalDistributedSystem IdsForId = null; /** * Mutex controls update of {@link #DMid} * @see #DMid * @see #IdsForId */ private static final Object DmidMutex = new Object(); /** * Read current {@link #DMid} and return it * @return current DMid */ private static String getId() { synchronized (DmidMutex) { InternalDistributedSystem ids = InternalDistributedSystem .getAnyInstance(); if (ids == null) { throw new DistributedSystemDisconnectedException("No distributed system"); } if (ids == IdsForId) { return DMid; } IdsForId = ids; DM dm = ids.getDistributionManager(); DMid = dm.getId().toString(); return DMid; } } /** * Returns a byte array which uses a static synchronized counter to ensure * uniqueness * */ private static byte[] generateGTid() { //Asif: The counter should be attached to the string inside Synch block StringBuffer sbuff = new StringBuffer(getId()); synchronized (GlobalTransaction.class) { if (mCounter == 99999) mCounter = 1; else ++mCounter; sbuff.append(String.valueOf(mCounter)); } sbuff.append('_').append(System.currentTimeMillis()); byte[] byte_array = sbuff.toString().getBytes(); return byte_array; } /** * A timer task cleaup method. This is called when Transaction timeout occurs. * On timeout the transaction is rolled back and the thread removed from * thread-Transaction Map. */ void expireGTX() { if (timedOut) return; // this method is only called by a single thread so this is safe timedOut = true; LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); try { if (writer.infoEnabled()) writer .info(LocalizedStrings.GlobalTransaction_TRANSACTION_0_HAS_TIMED_OUT, this); TransactionManagerImpl.getTransactionManager() .removeTranxnMappings(transactions); setStatus(Status.STATUS_NO_TRANSACTION); } catch (Exception e) { if (writer.severeEnabled()) writer.severe(LocalizedStrings.GlobalTransaction_GLOBATRANSACTION_EXPIREGTX_ERROR_OCCURED_WHILE_REMOVING_TRANSACTIONAL_MAPPINGS_0, e, e); } } /** * Set the transaction TimeOut of the Global Transaction Asif : It returns the * new expiry time for the GTX. * * @param seconds * @throws SystemException */ long setTransactionTimeoutForXARes(int seconds) throws SystemException { XAResource xar = null; boolean resetXATimeOut = true; Map.Entry entry; synchronized (this.resourceMap) { Iterator iterator = resourceMap.entrySet().iterator(); while (iterator.hasNext()) { entry= (Map.Entry)iterator.next(); xar = (XAResource)entry.getKey(); if (((Boolean)entry.getValue()).booleanValue()) { try { resetXATimeOut = xar.setTransactionTimeout(seconds); } catch (XAException e) { String exception = LocalizedStrings.GlobalTransaction_EXCEPTION_OCCURED_WHILE_TRYING_TO_SET_THE_XARESOURCE_TIMEOUT_DUE_TO_0_ERROR_CODE_1.toLocalizedString(new Object[] {e, Integer.valueOf(e.errorCode)}); LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (VERBOSE) writer.fine(exception); final SystemException ex = new SystemException(exception); ex.initCause(e); throw ex; } break; } } } long newExp = System.currentTimeMillis() + (seconds * 1000); if (!resetXATimeOut) newExp = -1; return newExp; } /** * Testmethod to provide the size of the resource map * */ int getResourceMapSize() { return this.resourceMap.size(); } /** * Returns the List of Transactions associated with this GlobalTransaction * Asif : Reduced the visibility */ List getTransactions() { return transactions; } long getExpirationTime() { return expirationTime; } void setTimeoutValue(long time) { expirationTime = time; } boolean isExpired() { return timedOut; } public int compare(GlobalTransaction other) { if (this == other) { return 0; } long compare = getExpirationTime() - other.getExpirationTime(); if (compare < 0) { return -1; } else if (compare > 0) { return 1; } // need to compare something else to break the tie to fix bug 39579 if (this.GTid.length < other.GTid.length) { return -1; } else if (this.GTid.length > other.GTid.length) { return 1; } // need to compare the bytes for (int i=0; i < this.GTid.length; i++) { if (this.GTid[i] < other.GTid[i]) { return -1; } else if (this.GTid[i] > other.GTid[i]) { return 1; } } // If we get here the GTids are the same! int myId = System.identityHashCode(this); int otherId = System.identityHashCode(other); if (myId < otherId) { return -1; } else if (myId > otherId) { return 1; } else { // we could just add another field to this class which has a value // obtained from a static atomic throw new IllegalStateException( LocalizedStrings.GlobalTransaction_COULD_NOT_COMPARE_0_TO_1 .toLocalizedString(new Object[] {this, other})); } } // public String toString() { // StringBuffer sb = new StringBuffer(); // sb.append("<"); // sb.append(super.toString()); // sb.append(" expireTime=" + getExpirationTime()); // sb.append(">"); // return sb.toString(); // } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy