com.sun.jts.CosTransactions.DelegatedTimeoutManager Maven / Gradle / Ivy
Show all versions of payara-micro Show documentation
/*
* 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.
*/
package com.sun.jts.CosTransactions;
import java.util.*;
import org.omg.CosTransactions.*;
import com.sun.jts.jtsxa.XID;
import com.sun.jts.trace.*;
import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;
import com.sun.jts.utils.LogFormatter;
/**
* This class records state for timing out transactions, and runs a thread
* which performs occasional checks to time out transactions. For each log
* location, which requires delegated recovery, an instance of this will be
* created. Done as part of delegated recovery support.
* th
*
* @version 0.01
*
*
* @see
*/
class DelegatedTimeoutManager {
/**
* 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 Hashtable pendingTimeouts = new Hashtable();
private Hashtable indoubtTimeouts = new Hashtable();
private DelegatedTimeoutThread timeoutThread = null;
private boolean timeoutActive = false;
private boolean quiescing = false;
private boolean isSetTimeout = false;
private String logPath = null;
/*
Logger to log transaction messages
*/
static Logger _logger = LogDomains.getLogger(DelegatedTimeoutManager.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;
* }
* }
**/
DelegatedTimeoutManager() {
}
DelegatedTimeoutManager(String logPath) {
this.logPath = logPath;
}
synchronized void initSetTimeout() {
if (isSetTimeout)
return;
isSetTimeout = true;
timeoutThread = new DelegatedTimeoutThread(this);
timeoutThread.start();
}
/**
* Sets the timeout for the transaction to the specified type and time in
* seconds.
*
* 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
*/
boolean setTimeout(Long localTID, int timeoutType,
int seconds) {
boolean result = true;
// Modify the timeout to the required type and value.
DelegatedTimeoutInfo 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 DelegatedTimeoutManager.ACTIVE_TIMEOUT :
if (!isSetTimeout) {
initSetTimeout();
}
timeoutInfo = new DelegatedTimeoutInfo();
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 DelegatedTimeoutInfo();
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;
}
return result;
}
/**
* Takes appropriate action for a timeout.
*
* The type fo timeout is given, and the transaction represented by the
* Coordinator and its local identifier.
*
* 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
*/
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 = DelegatedRecoveryManager.getLocalCoordinator(localTID, logPath);
if (coord == null) {
if(_logger.isLoggable(Level.FINER)) {
_logger.logp(Level.FINER,"DelegatedTimeoutManager","timeoutCoordinator()",
"DelegatedRecoveryManager.getLocalCoordinator() returned null,"+
"which means txn is done. Setting timeout type to CANCEL_TIMEOUT");
}
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 DelegatedTimeoutManager.ACTIVE_TIMEOUT :
if(_logger.isLoggable(Level.FINER)) {
_logger.logp(Level.FINER,"DelegatedTimeoutManager","timeoutCoordinator()",
"DelegatedTimeoutManager.timeoutCoordinator():case ACTIVE_TIMEOUT"+
"DelegatedRecoveryManager.getLocalCoordinator() returned non-null,"+
"which means txn is still around. Rolling back 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 DelegatedTimeoutManager.IN_DOUBT_TIMEOUT :
if(_logger.isLoggable(Level.FINER)) {
_logger.logp(Level.FINER,"DelegatedTimeoutManager","timeoutCoordinator()",
"DelegatedTimeoutManager.timeoutCoordinator():case IN_DOUBT_TIMEOUT"+
"DelegatedRecoveryManager.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.
*
* This is done to discover if any transactions have overrun their allotted
* time. Those which have are returned as an Enumeration.
*
* 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
*/
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()) {
DelegatedTimeoutInfo timeoutInfo = (DelegatedTimeoutInfo)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()) {
DelegatedTimeoutInfo timeoutInfo = (DelegatedTimeoutInfo)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.
*/
XID[] getInDoubtXids() {
synchronized (indoubtTimeouts) {
Vector inDoubtList = new Vector();
Enumeration timeouts = indoubtTimeouts.elements();
while (timeouts.hasMoreElements()) {
DelegatedTimeoutInfo timeoutInfo = (DelegatedTimeoutInfo) 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 =
DelegatedRecoveryManager.getLocalCoordinator(timeoutInfo.localTID, logPath);
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
*/
long timeLeft(Long localTID) {
DelegatedTimeoutInfo timeoutInfo = (DelegatedTimeoutInfo) pendingTimeouts.get(localTID);
if (timeoutInfo == null)
timeoutInfo = (DelegatedTimeoutInfo) 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
*/
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;
}
}
}
/**
* 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 DelegatedTimeoutInfo 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
*
*
* @see
*/
class DelegatedTimeoutThread extends Thread {
private int TIMEOUT_INTERVAL ;
private DelegatedTimeoutManager tmoutMgr = null;
static Logger _logger = LogDomains.getLogger(DelegatedTimeoutThread.class, LogDomains.TRANSACTION_LOGGER);
/**
* TimeoutThread constructor.
*
* 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
*/
DelegatedTimeoutThread(DelegatedTimeoutManager timeoutMgr) {
setName("Delegated JTS Timeout Thread"/*#Frozen*/);
setDaemon(true);
tmoutMgr = timeoutMgr;
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 = tmoutMgr.checkTimeouts();
// Now we must go through the list, telling each
// timed-out Coordinator to do something appropriate.
if (timedOut != null) {
while (timedOut.hasMoreElements()) {
DelegatedTimeoutInfo timeoutInfo =
(DelegatedTimeoutInfo) 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.
tmoutMgr.
timeoutCoordinator(timeoutInfo.localTID,
timeoutInfo.timeoutType);
}
}
}
} catch (InterruptedException exc) {
_logger.log(Level.INFO,"jts.time_out_thread_stopped");
}
}
}