/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ //---------------------------------------------------------------------------- // // Module: TimeoutManager.java // // Description: Transaction time-out manager. // // Product: com.sun.jts.CosTransactions // // Author: Simon Holdsworth // // Date: March, 1997 // // Copyright (c): 1995-1997 IBM Corp. // // The source code for this program is not published or otherwise divested // of its trade secrets, irrespective of what has been deposited with the // U.S. Copyright Office. // // This software contains confidential and proprietary information of // IBM Corp. //---------------------------------------------------------------------------- //Portions Copyright [2016] [Payara Foundation] package com.sun.jts.CosTransactions; import java.util.*; import org.omg.CosTransactions.*; import com.sun.jts.jtsxa.XID; import java.util.logging.Logger; import java.util.logging.Level; import com.sun.logging.LogDomains; /** * This class records state for timing out transactions, and runs a thread * which performs occasional checks to time out transactions. * * @version 0.01 * * @author Simon Holdsworth, IBM Corporation * * @see */ //---------------------------------------------------------------------------- // CHANGE HISTORY // // Version By Change Description // 0.01 SAJH Initial implementation. //---------------------------------------------------------------------------- class TimeoutManager { /** * Constants which define the types of timeout possible. */ static final int CANCEL_TIMEOUT = 0; static final int NO_TIMEOUT = 0; static final int ACTIVE_TIMEOUT = 1; static final int IN_DOUBT_TIMEOUT = 2; /** * this attribute indicates whether initialisation has been started. */ private static boolean initialised = false; private static Hashtable pendingTimeouts = new Hashtable(); private static Hashtable indoubtTimeouts = new Hashtable(); private static TimeoutThread timeoutThread = null; private static boolean timeoutActive = false; private static boolean quiescing = false; private static boolean isSetTimeout = false; /* Logger to log transaction messages */ static Logger _logger = LogDomains.getLogger(TimeoutManager.class, LogDomains.TRANSACTION_LOGGER); /** * Initialises the static state of the TimeoutManager class. * * @param * * @return * * @see */ synchronized static void initialise() { // If already initialised, return immediately. if (initialised) { return; } initialised = true; // Start the timeout thread. if (!timeoutActive && timeoutThread == null) { // timeoutThread = new TimeoutThread(); // timeoutThread.start(); timeoutActive = true; } } static synchronized void initSetTimeout() { if (isSetTimeout) return; isSetTimeout = true; // to prevent classloader explicitly set context classloader timeoutThread = new TimeoutThread(); timeoutThread.setContextClassLoader(timeoutThread.getClass().getClassLoader()); timeoutThread.start(); } /** * Sets the timeout for the transaction to the specified type and time in * seconds. * <p> * If the type is none, the timeout for the transaction is * cancelled, otherwise the current timeout for the transaction is modified * to be of the new type and duration. * * @param localTID The local identifier for the transaction. * @param timeoutType The type of timeout to establish. * @param seconds The length of the timeout. * * @return Indicates success of the operation. * * @see */ static boolean setTimeout(Long localTID, int timeoutType, int seconds) { boolean result = true; // Modify the timeout to the required type and value. if (timeoutActive) { TimeoutInfo timeoutInfo = null; switch (timeoutType) { // If the new type is active or in_doubt, then create a // new TimeoutInfo if necessary, and set up the type and interval. case TimeoutManager.ACTIVE_TIMEOUT : if (!isSetTimeout) { initSetTimeout(); } timeoutInfo = new TimeoutInfo(); timeoutInfo.expireTime = new Date().getTime() + seconds * 1000L; timeoutInfo.localTID = localTID; timeoutInfo.timeoutType = timeoutType; pendingTimeouts.put(localTID,timeoutInfo); break; case TimeoutManager.IN_DOUBT_TIMEOUT : if (!isSetTimeout) { initSetTimeout(); // isSetTimeout = true; } timeoutInfo = new TimeoutInfo(); timeoutInfo.expireTime = new Date().getTime() + seconds * 1000L; timeoutInfo.localTID = localTID; timeoutInfo.timeoutType = timeoutType; indoubtTimeouts.put(localTID,timeoutInfo); break; // For any other type, remove the timeout if there is one. default: if (!isSetTimeout) break; result = (pendingTimeouts.remove(localTID) != null); if (!result) result = (indoubtTimeouts.remove(localTID) != null); // If the transaction service is quiescing and // there are no more pending timeouts, // deactivate timeout and stop the timeout thread. if (quiescing && pendingTimeouts.isEmpty() && indoubtTimeouts.isEmpty()) { timeoutThread.stop(); timeoutActive = false; // pendingTimeouts = null; } break; } } else { // If timeouts are not active, just return false. result = false; } return result; } /** * Takes appropriate action for a timeout. * <p> * The type fo timeout is given, and the transaction represented by the * Coordinator and its local identifier. * <p> * This method does not reference the TimeoutManager's state directly * and so does not need to be synchronized. * * @param localTID The local identifier for the transaction. * @param timeoutType The type of timeout. * * @return * * @see */ static void timeoutCoordinator(Long localTID, int timeoutType) { // Look up the Coordinator for the transaction. // If there is none, then the transaction has already gone. // Otherwise do something with the transaction. CoordinatorImpl coord = RecoveryManager.getLocalCoordinator(localTID); if (coord == null) { if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","timeoutCoordinator()", "RecoveryManager.getLocalCoordinator() returned null,"+ "which means txn is done. Setting timeout type to CANCEL_TIMEOUT"); } TimeoutManager.setTimeout(localTID, TimeoutManager.CANCEL_TIMEOUT, 0); } else { synchronized (coord) { boolean[] isRoot = new boolean[1]; switch (timeoutType) { // If active, then attempt to roll the transaction back. case TimeoutManager.ACTIVE_TIMEOUT : if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","timeoutCoordinator()", "TimeoutManager.timeoutCoordinator():case ACTIVE_TIMEOUT"+ "RecoveryManager.getLocalCoordinator() returned non-null,"+ "which means txn is still around. Marking for Rollback the"+ "transaction...: GTID is : " + ((TopCoordinator)coord).superInfo.globalTID.toString()); } try { // coord.rollback(true); coord.rollback_only(); } catch (Throwable exc) {} break; // If in doubt, it must be a TopCoordinator. // In that case replay_completion needs to be driven. // This is done by telling the TopCoordinator to act as // if in recovery. The result is then used to // determine what to do with the Coordinator. case TimeoutManager.IN_DOUBT_TIMEOUT : if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","timeoutCoordinator()", "TimeoutManager.timeoutCoordinator():case IN_DOUBT_TIMEOUT"+ "RecoveryManager.getLocalCoordinator() returned non-null,"+ "which means txn is still around. Invoking recover(boolean)"+ "on TopCoordinator...: GTID is: "+ ((TopCoordinator)coord).superInfo.globalTID.toString()); } Status state = ((TopCoordinator) coord).recover(isRoot); if (state == Status.StatusUnknown) { // If the outcome is not currently known, we do // nothing with the transaction, as we expect to // eventually get an outcome from the parent. // GDH put out warning in case this state // continues for a long time. _logger.log(Level.WARNING, "jts.transaction_resync_from_orginator_failed"); } else if (state == Status.StatusCommitted) { // For committed or rolled back, proceed with // completion of the transaction, regardless of whether // it is the root or a subordinate. This will // result in the removal of the in-doubt timeout. try { ((TopCoordinator)coord).commit(); if (isRoot[0]) { ((TopCoordinator) coord). afterCompletion(state); } } catch (Throwable exc) {} } else { // By default, roll the transaction back. try { ((TopCoordinator) coord).rollback(true); if (isRoot[0]) { ((TopCoordinator) coord). afterCompletion(Status.StatusRolledBack); } } catch (Throwable exc) {} } break; default: // Otherwise do nothing. break; } } } } /** * Periodically checks the existing timeouts. * <p> * This is done to discover if any transactions have overrun their allotted * time. Those which have are returned as an Enumeration. * <p> * Note that this method should not do anything that will cause a * synchronized method in the RecoveryManager to be called, as this could * cause a deadlock when RecoveryManager methods on other threads call * setTimeout. * * @param * * @return The information for transactions which have timed out. * * @see */ static Enumeration checkTimeouts() { if (!isSetTimeout) return null; Enumeration result = null; // When woken up, go through all current timeouts and identify those // which have expired. if (timeoutActive && ((pendingTimeouts.size() != 0) || (indoubtTimeouts.size() != 0))) { Vector timedOut = null; Enumeration timeouts = null; synchronized (pendingTimeouts) { timeouts = pendingTimeouts.elements(); while (timeouts.hasMoreElements()) { TimeoutInfo timeoutInfo = (TimeoutInfo)timeouts.nextElement(); // For each timeout in the list, check whether it has expired. // If so, look up the Coordinator and roll it back. if (new Date().getTime() > timeoutInfo.expireTime) { // Add the TimeoutInfo to the queue of //those that have timed out. if (timedOut == null) { timedOut = new Vector(); } timedOut.addElement(timeoutInfo); } } } synchronized (indoubtTimeouts) { timeouts = indoubtTimeouts.elements(); while (timeouts.hasMoreElements()) { TimeoutInfo timeoutInfo = (TimeoutInfo)timeouts.nextElement(); // For each timeout in the list, check whether it has expired. // If so, look up the Coordinator and roll it back. if (new Date().getTime() > timeoutInfo.expireTime) { // Add the TimeoutInfo to the queue of //those that have timed out. if (timedOut == null) { timedOut = new Vector(); } timedOut.addElement(timeoutInfo); } } } // Enumerate the transactions which have timed out. if (timedOut != null) { result = timedOut.elements(); } } // The remainder of the timeout processing is not carried out here // because we would get deadlocked with addCoordinator or // removeCoordinator that also update the timeout list. Hence the // returned enumeration, which may be processed with // no concurrency control. return result; } /** * @return a set of in-doubt transaction ids. */ static XID[] getInDoubtXids() { synchronized (indoubtTimeouts) { Vector inDoubtList = new Vector(); Enumeration timeouts = indoubtTimeouts.elements(); while (timeouts.hasMoreElements()) { TimeoutInfo timeoutInfo = (TimeoutInfo) timeouts.nextElement(); // Look up the Coordinator for the transaction. // If there is none, then the transaction has already gone. // Otherwise do something with the transaction. CoordinatorImpl coord = RecoveryManager.getLocalCoordinator(timeoutInfo.localTID); if (coord != null) { XID xid = new XID(); xid.copy(coord.getGlobalTID()); inDoubtList.addElement(xid); } } return (XID[]) inDoubtList.toArray(new XID[] {}); } } /** * Returns the amount of time left before the given transaction times out. * * @param localTID The local identifier for the transaction. * * @return The time left. If there is no timeout for the transaction, * this value will be negative. If the timeout period has been * exceeded, this value will be zero. * * @see */ static long timeLeft(Long localTID) { TimeoutInfo timeoutInfo = (TimeoutInfo) pendingTimeouts.get(localTID); if (timeoutInfo == null) timeoutInfo = (TimeoutInfo) indoubtTimeouts.get(localTID); long result = -1; if (timeoutInfo != null) { result = timeoutInfo.expireTime - new Date().getTime(); if (result < 0) { result = 0; } } return result; } /** * Informs the TimeoutManager that the transaction service * is being shut down. For immediate shutdown, the timeout thread is * stopped and all timeout information discarded. * * For quiesce, the timeout thread is stopped when there are no running * transactions left. * * @param immediate Indicates whether to stop immediately. * * @return * * @see */ static void shutdown(boolean immediate) { // For immediate, kill the timeout thread and throw // away all information. Also, if there are no pending // timeouts, there is nothing to quiesce so // shutdown immediately regardless. if (immediate || pendingTimeouts == null || pendingTimeouts.isEmpty()) { if (timeoutThread != null) { timeoutThread.stop(); } if (pendingTimeouts != null) { pendingTimeouts.clear(); } pendingTimeouts = null; timeoutThread = null; timeoutActive = false; } else { quiescing = true; } } /** * Reports the contents of the TimeoutManager tables. *$Only required for debug. * * @param immediate Indicates whether to stop immediately. * * @return * * @see */ /* static void report() { // Report on pendingTimeouts. if (pendingTimeouts.size() > 0) { if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","report()", "TimeoutManager.pendingTimeouts non-empty"); } Enumeration keys = pendingTimeouts.keys(); while (keys.hasMoreElements()) { Long localTID = (Long) keys.nextElement(); TimeoutInfo timeInfo = (TimeoutInfo) pendingTimeouts.get(localTID); if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","report()", "localTid :"+localTID+" -> " + timeInfo); } } } else { if(_logger.isLoggable(Level.FINER)) { _logger.logp(Level.FINER,"TimeoutManager","report()", "TimeoutManager.pendingTimeouts empty"); } } } */ } /** * This class records information for a timeout for a transaction. * * @version 0.1 * * @author Simon Holdsworth, IBM Corporation * * @see */ //---------------------------------------------------------------------------- // CHANGE HISTORY // // Version By Change Description // 0.1 SAJH Initial implementation. //---------------------------------------------------------------------------- class TimeoutInfo extends Object { Long localTID = null; long expireTime = 0; int timeoutType = TimeoutManager.NO_TIMEOUT; } /** * This class represents a thread on which the TimeoutManager can perform * timeout checking. * * @version 0.01 * * @author Simon Holdsworth, IBM Corporation * * @see */ //---------------------------------------------------------------------------- // CHANGE HISTORY // // Version By Change Description // 0.01 SAJH Initial implementation. //---------------------------------------------------------------------------- class TimeoutThread extends Thread { private int TIMEOUT_INTERVAL ; static Logger _logger = LogDomains.getLogger(TimeoutThread.class, LogDomains.TRANSACTION_LOGGER); /** * TimeoutThread constructor. * <p> * This sets the thread name, and sets the thread to be a daemon thread so * that it does not prevent the process from terminating. * * @param * * @return * * @see */ TimeoutThread() { setName("JTS Timeout Thread"/*#Frozen*/); setDaemon(true); try{ String timeout_interval = Configuration.getPropertyValue(Configuration.TIMEOUT_INTERVAL); if(timeout_interval!=null){ TIMEOUT_INTERVAL= Integer.parseInt(timeout_interval); TIMEOUT_INTERVAL*=1000; if(TIMEOUT_INTERVAL<10000) TIMEOUT_INTERVAL=10000; } else{ TIMEOUT_INTERVAL=10000; } }catch(Exception e){ TIMEOUT_INTERVAL=10000; } } /** * Performs timeout checking on a regular basis (every ten seconds or so). * * @param * * @return * * @see */ public void run() { try { while (true) { // Sleep for a while between checks. Thread.sleep(TIMEOUT_INTERVAL); // Perform timeout checks, getting a list of timed-out // transactions. Enumeration timedOut = TimeoutManager.checkTimeouts(); // Now we must go through the list, telling each // timed-out Coordinator to do something appropriate. if (timedOut != null) { while (timedOut.hasMoreElements()) { TimeoutInfo timeoutInfo = (TimeoutInfo) timedOut.nextElement(); // Look up the Coordinator and tell it to roll back // if it still exists. Note that we rely on the // Coordinator calling removeCoordinator when it // has finished, which will remove the timeout from // the list, and remove other associations as well. TimeoutManager. timeoutCoordinator(timeoutInfo.localTID, timeoutInfo.timeoutType); } } } } catch (InterruptedException exc) { _logger.log(Level.INFO,"jts.time_out_thread_stopped"); } } }