All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
bitronix.tm.recovery.RecoveryHelper Maven / Gradle / Ivy
/*
* Copyright (C) 2006-2013 Bitronix Software (http://www.bitronix.be)
*
* 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.
*/
package bitronix.tm.recovery;
import bitronix.tm.BitronixXid;
import bitronix.tm.TransactionManagerServices;
import bitronix.tm.internal.LogDebugCheck;
import bitronix.tm.internal.XAResourceHolderState;
import bitronix.tm.utils.Decoder;
import bitronix.tm.utils.Uid;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
/**
* Reovery helper methods.
*
* @author Ludovic Orban
*/
public class RecoveryHelper
{
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(RecoveryHelper.class.toString());
private static final String XIDS_ON = " xid(s) on ";
private static final String UNABLE_TO_COMMIT_INDOUBT = "unable to commit in-doubt branch on resource ";
private static final String EXTRA_ERROR = ", extra error=";
private static final String ERROR = " - error=";
private static final String UNABLE_TO_ROLLBACK_INDOUBT = "unable to rollback aborted in-doubt branch on resource ";
private RecoveryHelper()
{
//Nothing needed
}
/**
* Run the recovery process on the target resource.
*
* @param xaResourceHolderState
* the {@link XAResourceHolderState} to recover.
*
* @return a Set of BitronixXids.
*
* @throws javax.transaction.xa.XAException
* if {@link XAResource#recover(int)} calls fail.
*/
public static Set recover(XAResourceHolderState xaResourceHolderState) throws XAException
{
Set xids = new HashSet<>();
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovering with STARTRSCAN");
}
int xidCount;
try
{
xidCount = recover(xaResourceHolderState, xids, XAResource.TMSTARTRSCAN);
}
catch (XAException ex)
{
if (xaResourceHolderState.getIgnoreRecoveryFailures())
{
if (LogDebugCheck.isDebugEnabled())
{
log.log(Level.FINER, "ignoring recovery failure on resource " + xaResourceHolderState, ex);
}
return Collections.emptySet();
}
throw ex;
}
if (LogDebugCheck.isDebugEnabled())
{
log.finer("STARTRSCAN recovered " + xidCount + XIDS_ON + xaResourceHolderState);
}
try
{
while (xidCount > 0)
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovering with NOFLAGS");
}
xidCount = recover(xaResourceHolderState, xids, XAResource.TMNOFLAGS);
if (LogDebugCheck.isDebugEnabled())
{
log.finer("NOFLAGS recovered " + xidCount + XIDS_ON + xaResourceHolderState);
}
}
}
catch (XAException ex)
{
if (LogDebugCheck.isDebugEnabled())
{
log.log(Level.FINER, "NOFLAGS recovery call failed", ex);
}
}
try
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovering with ENDRSCAN");
}
xidCount = recover(xaResourceHolderState, xids, XAResource.TMENDRSCAN);
if (LogDebugCheck.isDebugEnabled())
{
log.finer("ENDRSCAN recovered " + xidCount + XIDS_ON + xaResourceHolderState);
}
}
catch (XAException ex)
{
if (LogDebugCheck.isDebugEnabled())
{
log.log(Level.FINER, "ENDRSCAN recovery call failed", ex);
}
}
return xids;
}
/**
* Call {@link XAResource#recover(int)} on the resource and fill the alreadyRecoveredXids
Set
* with recovered {@link BitronixXid}s.
* Step 1.
*
* @param resourceHolderState
* the {@link XAResourceHolderState} to recover.
* @param alreadyRecoveredXids
* a set of {@link Xid}s already recovered from this resource in this recovery session.
* @param flags
* any combination of {@link XAResource#TMSTARTRSCAN}, {@link XAResource#TMNOFLAGS} or {@link XAResource#TMENDRSCAN}.
*
* @return the amount of recovered {@link Xid}.
*
* @throws javax.transaction.xa.XAException
* if {@link XAResource#recover(int)} call fails.
*/
private static int recover(XAResourceHolderState resourceHolderState, Set alreadyRecoveredXids, int flags) throws XAException
{
Xid[] xids = resourceHolderState.getXAResource()
.recover(flags);
if (xids == null)
{
return 0;
}
boolean currentNodeOnly = TransactionManagerServices.getConfiguration()
.isCurrentNodeOnlyRecovery();
Set freshlyRecoveredXids = new HashSet<>();
for (Xid xid : xids)
{
if (xid.getFormatId() != BitronixXid.FORMAT_ID)
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("skipping non-bitronix XID " + xid + "(format ID: " + xid.getFormatId() +
" GTRID: " + new Uid(xid.getGlobalTransactionId()) + "BQUAL: " + new Uid(xid.getBranchQualifier()) + ")");
}
continue;
}
BitronixXid bitronixXid = new BitronixXid(xid);
if (currentNodeOnly)
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovering XIDs generated by this node only - recovered XIDs' GTRID must contain this JVM uniqueId");
}
byte[] extractedServerId = bitronixXid.getGlobalTransactionIdUid()
.extractServerId();
byte[] jvmUniqueId = TransactionManagerServices.getConfiguration()
.buildServerIdArray();
if (extractedServerId.length == 0)
{
log.severe("skipping XID " + bitronixXid + " as its GTRID's serverId is null. It looks like the disk journal is corrupted!");
continue;
}
if (!Arrays.equals(jvmUniqueId, extractedServerId))
{
String extractedServerIdString = new String(extractedServerId);
String jvmUniqueIdString = new String(jvmUniqueId);
if (LogDebugCheck.isDebugEnabled())
{
log.finer("skipping XID " + bitronixXid + " as its GTRID's serverId <" + extractedServerIdString + "> does not match this JVM unique ID <" +
jvmUniqueIdString + ">");
}
continue;
}
}
else
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovering all XIDs regardless of this JVM uniqueId");
}
}
if (alreadyRecoveredXids.contains(bitronixXid))
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("already recovered XID " + bitronixXid + ", skipping it");
}
continue;
}
if (freshlyRecoveredXids.contains(bitronixXid))
{
log.warning("resource " + resourceHolderState.getUniqueName() + " recovered two identical XIDs within the same recover call: " + bitronixXid);
continue;
}
if (LogDebugCheck.isDebugEnabled())
{
log.finer("recovered " + bitronixXid);
}
freshlyRecoveredXids.add(bitronixXid);
} // for i < xids.length
alreadyRecoveredXids.addAll(freshlyRecoveredXids);
return freshlyRecoveredXids.size();
}
/**
* Commit the specified branch of a dangling transaction.
*
* @param xaResourceHolderState
* the {@link XAResourceHolderState} to commit the branch on.
* @param xid
* the {@link Xid} to commit.
*
* @return true when commit was successful.
*/
public static boolean commit(XAResourceHolderState xaResourceHolderState, Xid xid)
{
String uniqueName = xaResourceHolderState.getUniqueName();
boolean success = true;
boolean forget = false;
try
{
xaResourceHolderState.getXAResource()
.commit(xid, false);
}
catch (XAException ex)
{
String extraErrorDetails = TransactionManagerServices.getExceptionAnalyzer()
.extractExtraXAExceptionDetails(ex);
if (ex.errorCode == XAException.XAER_NOTA)
{
log.log(Level.SEVERE, UNABLE_TO_COMMIT_INDOUBT + uniqueName + " - error=XAER_NOTA" +
(extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) + ". Forgotten heuristic?", ex);
}
else if (ex.errorCode == XAException.XA_HEURCOM)
{
log.info(UNABLE_TO_COMMIT_INDOUBT + uniqueName + ERROR +
Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) +
". Heuristic decision compatible with the global state of this transaction.");
forget = true;
}
else if (ex.errorCode == XAException.XA_HEURHAZ || ex.errorCode == XAException.XA_HEURMIX || ex.errorCode == XAException.XA_HEURRB)
{
log.severe(UNABLE_TO_COMMIT_INDOUBT + uniqueName + ERROR +
Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) +
". Heuristic decision incompatible with the global state of this transaction!");
forget = true;
success = false;
}
else
{
log.log(Level.SEVERE, UNABLE_TO_COMMIT_INDOUBT + uniqueName +
ERROR + Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) + ".", ex);
success = false;
}
}
if (forget)
{
processForget(xaResourceHolderState, xid, uniqueName);
}
return success;
}
/**
* Goes through the forget phase
*
* @param xaResourceHolderState
* resource
* @param xid
* yes
* @param uniqueName
* Single unique name
*/
private static void processForget(XAResourceHolderState xaResourceHolderState, Xid xid, String uniqueName)
{
try
{
if (LogDebugCheck.isDebugEnabled())
{
log.finer("forgetting XID " + xid + " on resource " + uniqueName);
}
xaResourceHolderState.getXAResource()
.forget(xid);
}
catch (XAException ex)
{
String extraErrorDetails = TransactionManagerServices.getExceptionAnalyzer()
.extractExtraXAExceptionDetails(ex);
log.log(Level.SEVERE, "unable to forget XID " + xid + " on resource " + uniqueName + ", error=" + Decoder.decodeXAExceptionErrorCode(ex) +
(extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails), ex);
}
}
/**
* Rollback the specified branch of a dangling transaction.
*
* @param xaResourceHolderState
* the {@link XAResourceHolderState} to rollback the branch on.
* @param xid
* the {@link Xid} to rollback.
*
* @return true when rollback was successful.
*/
public static boolean rollback(XAResourceHolderState xaResourceHolderState, Xid xid)
{
String uniqueName = xaResourceHolderState.getUniqueName();
boolean success = true;
boolean forget = false;
try
{
xaResourceHolderState.getXAResource()
.rollback(xid);
}
catch (XAException ex)
{
String extraErrorDetails = TransactionManagerServices.getExceptionAnalyzer()
.extractExtraXAExceptionDetails(ex);
if (ex.errorCode == XAException.XAER_NOTA)
{
log.log(Level.SEVERE, UNABLE_TO_ROLLBACK_INDOUBT + uniqueName + " - error=XAER_NOTA" +
(extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) + ". Forgotten heuristic?", ex);
}
else if (ex.errorCode == XAException.XA_HEURRB)
{
log.info(UNABLE_TO_ROLLBACK_INDOUBT + uniqueName + ERROR +
Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) +
". Heuristic decision compatible with the global state of this transaction.");
forget = true;
}
else if (ex.errorCode == XAException.XA_HEURHAZ || ex.errorCode == XAException.XA_HEURMIX || ex.errorCode == XAException.XA_HEURCOM)
{
log.severe(UNABLE_TO_ROLLBACK_INDOUBT + uniqueName + ERROR +
Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) +
". Heuristic decision incompatible with the global state of this transaction!");
forget = true;
success = false;
}
else
{
log.log(Level.SEVERE, UNABLE_TO_ROLLBACK_INDOUBT + uniqueName + ERROR +
Decoder.decodeXAExceptionErrorCode(ex) + (extraErrorDetails == null ? "" : EXTRA_ERROR + extraErrorDetails) + ".", ex);
success = false;
}
}
if (forget)
{
processForget(xaResourceHolderState, xid, uniqueName);
}
return success;
}
}