/** * Normally returns the current state of the log entry. However, this is never called during * normal (non-recovery) execution. Therefore, the overhead of having to scan all of the logs (if * it's not one we're using) is minimal. */ public int currentState(Uid objUid, String tName) throws ObjectStoreException { InputObjectState ios = new InputObjectState(); /* * TODO * * It's possible that the entry has been marked to be deleted but * that the removal entry hasn't been written yet. We could check the * async cache. However, since we really only care about this during * recovery, it's not going to cause us problems anyway. */ if (allObjUids(tName, ios, StateStatus.OS_UNKNOWN)) { Uid tempUid = new Uid(Uid.nullUid()); do { try { tempUid = UidHelper.unpackFrom(ios); } catch (final Exception ex) { ex.printStackTrace(); return StateStatus.OS_UNKNOWN; } if (tempUid.equals(objUid)) return StateStatus.OS_COMMITTED; } while (tempUid.notEquals(Uid.nullUid())); return StateStatus.OS_UNKNOWN; } else return StateStatus.OS_UNKNOWN; }
private static XID getXid(Uid uid, Uid branch, int formatId, Integer eisName) throws IllegalStateException { if (uid == null) { throw new IllegalStateException(); } XID xid = new XID(); xid.formatID = formatId; // gtrid is uid byte form followed by as many chars of the node name as will fit. byte[] gtridUid = uid.getBytes(); if (gtridUid.length > XID.MAXGTRIDSIZE) { throw new IllegalStateException(); // Uid is too long!!!! } String nodeName = TxControl.getXANodeName(); int nodeNameLengthToUse = nodeName.getBytes().length; xid.gtrid_length = gtridUid.length + nodeNameLengthToUse; // src, srcPos, dest, destPos, length System.arraycopy(gtridUid, 0, xid.data, 0, gtridUid.length); System.arraycopy(nodeName.getBytes(), 0, xid.data, gtridUid.length, nodeNameLengthToUse); if (branch.notEquals(Uid.nullUid())) { // bqual is uid byte form plus EIS name. byte[] bqualUid = branch.getBytes(); if (bqualUid.length > XID.MAXBQUALSIZE) { throw new IllegalStateException(); // Uid is too long!!!! } int spareBqualBytes = XID.MAXBQUALSIZE - (bqualUid.length + 4); xid.bqual_length = bqualUid.length + 4 + 4; // src, srcPos, dest, destPos, length int offset = xid.gtrid_length; System.arraycopy(bqualUid, 0, xid.data, offset, bqualUid.length); setEisName(xid, eisName); } else { /* * Note: for some dbs we seem to be able to get * away with setting the size field to the size * of the actual branch. However, for Oracle, * it appears as though it must always be 64. * (At least for zero branches.) */ xid.data[xid.gtrid_length] = (byte) 0; xid.bqual_length = 64; } return xid; }
public static Xid getXid(Uid uid, boolean branch, int formatId) throws IllegalStateException { XID xid; if (branch) xid = getXid(uid, new Uid(), formatId, null); else xid = getXid(uid, Uid.nullUid(), formatId, null); return new XidImple(xid); }
public static void setBranchUID(XID xid, Uid uid) { if (xid == null || xid.formatID != FORMAT_ID) { return; } byte[] bqual = uid.getBytes(); System.arraycopy(bqual, 0, xid.data, xid.gtrid_length, Uid.UID_SIZE); }
public InputObjectState() { if (tsLogger.logger.isTraceEnabled()) { tsLogger.logger.trace("InputObjectState::InputObjectState()"); } bufferUid = new Uid(Uid.nullUid()); super._valid = false; imageType = null; }
public static Uid getUid(XID xid) { if (xid == null || xid.formatID != FORMAT_ID) { return Uid.nullUid(); } // The Uid byte are the first in the data array, so we just pass // in the whole thing and the additional trailing data is ignored. Uid tx = new Uid(xid.data); return tx; }
private final void listLogs(String type) throws IOException { InputObjectState buff = new InputObjectState(); try { if (StoreManager.getRecoveryStore() .allObjUids(TransactionTypeManager.getInstance().getTransactionType(type), buff)) { Uid u = null; do { u = UidHelper.unpackFrom(buff); if (Uid.nullUid().notEquals(u)) { System.out.println("Log: " + u); } } while (Uid.nullUid().notEquals(u)); } } catch (final ObjectStoreException ex) { throw new IOException(); } }
protected static OSRecordHolder readObjectStoreRecord(String type) { try { RecoveryStore recoveryStore = StoreManager.getRecoveryStore(); InputObjectState states = new InputObjectState(); if (recoveryStore.allObjUids(type, states) && states.notempty()) { Uid uid = UidHelper.unpackFrom(states); if (uid.notEquals(Uid.nullUid())) { InputObjectState ios = recoveryStore.read_committed(uid, type); return new OSRecordHolder(uid, type, ios); } } } catch (Exception e) { e.printStackTrace(); } return null; }
private static void clearObjectStore(String type) { try { RecoveryStore recoveryStore = StoreManager.getRecoveryStore(); InputObjectState states = new InputObjectState(); if (recoveryStore.allObjUids(type, states) && states.notempty()) { boolean finished = false; do { Uid uid = UidHelper.unpackFrom(states); if (uid.notEquals(Uid.nullUid())) { recoveryStore.remove_committed(uid, type); } else { finished = true; } } while (!finished); } } catch (Exception e) { e.printStackTrace(); } }
public static Uid getBranchUid(XID xid) { if (xid == null || xid.formatID != FORMAT_ID) { return Uid.nullUid(); } byte[] bqual = new byte[xid.bqual_length]; System.arraycopy(xid.data, xid.gtrid_length, bqual, 0, xid.bqual_length); // The Uid byte are the first in the data array, so we just pass // in the whole thing and the additional trailing data is ignored. Uid tx = new Uid(bqual); return tx; }
public final InputObjectState allObjUids() throws ObjectStoreException { OutputObjectState state = new OutputObjectState(); Iterator<Uid> iter = _ids.keySet().iterator(); try { while (iter.hasNext()) { UidHelper.packInto(iter.next(), state); } // don't forget to null terminate UidHelper.packInto(Uid.nullUid(), state); } catch (final IOException ex) { throw new ObjectStoreException(ex); } return new InputObjectState(state); }
private final boolean supportedLog(String logID) throws ObjectStoreException, IOException { Uid id = new Uid(logID); if (id.equals(Uid.nullUid())) return false; ObjectStoreIterator iter = new ObjectStoreIterator( StoreManager.getRecoveryStore(), TransactionTypeManager.getInstance().getTransactionType(_transactionType)); Uid u; do { u = iter.iterate(); if (u.equals(id)) return true; } while (Uid.nullUid().notEquals(u)); return false; }
public LogRecordWrapper(Uid uid) { super(null); _uidWrapper.setName( "jboss.jta:type=ObjectStore,itype=uninitialised,puid=" + uid.fileStringForm()); }
static XID getXid(Uid uid, boolean branch, Integer eisName) throws IllegalStateException { if (branch) return getXid(uid, new Uid(), FORMAT_ID, eisName); else return getXid(uid, Uid.nullUid(), FORMAT_ID, eisName); }
/** * This is a recovery-only method and should not be called during normal execution. As such we * need to load in all of the logs we can find that aren't already loaded (or activated). */ public boolean allObjUids(String tName, InputObjectState state, int match) throws ObjectStoreException { /* * match will always be OS_COMMITTED since that's all we ever write for * the logs. */ // in case of asynchronous removals trigger the purger now. _purger.trigger(); /* * Get a list of logs. Load them in to memory if we aren't already * working on them/it. But we can prune the entry once we're * finished or the memory footprint will grow. We should do this * for all frozen entries eventually too. */ InputObjectState logs = new InputObjectState(); OutputObjectState objUids = new OutputObjectState(); /* * We never call this method except during recovery. As such we shouldn't * need to worry about optimizations such as checking whether or not the * log is in current working memory. */ if (!super.allObjUids(tName, logs, match)) return false; else { /* * Now we have all of the log names let's attach to each one * and locate the committed instances (not deleted.) */ Uid logName = new Uid(Uid.nullUid()); try { do { logName = UidHelper.unpackFrom(logs); if (logName.notEquals(Uid.nullUid())) { /* * Could check to see if log is in current working memory. */ /* * TODO * * First purge the log if we can, but we need to know that * we're not playing with an instance that is being manipulated * from another VM instance. */ ArrayList<InputObjectState> txs = scanLog(logName, tName); if (txs.size() > 0) { for (int i = 0; i < txs.size(); i++) { UidHelper.packInto(txs.get(i).stateUid(), objUids); } } } } while (logName.notEquals(Uid.nullUid())); // remember null terminator UidHelper.packInto(Uid.nullUid(), objUids); state.setBuffer(objUids.buffer()); } catch (final IOException ex) { ex.printStackTrace(); return false; } return true; } }
/** * write_state saves the ObjectState in a file named by the type and Uid of the ObjectState. If * the second argument is SHADOW, then the file name is different so that a subsequent * commit_state invocation will rename the file. * * <p>We need to make sure that each entry is written to the next empty location in the log even * if there's already an entry for this tx. */ protected boolean write_state(Uid objUid, String tName, OutputObjectState state, int ft) throws ObjectStoreException { if (tsLogger.logger.isTraceEnabled()) { tsLogger.logger.trace( "ShadowingStore.write_state(" + objUid + ", " + tName + ", " + StateType.stateTypeString(ft) + ")"); } String fname = null; File fd = null; if (tName != null) { int imageSize = (int) state.length(); byte[] uidString = objUid.stringForm().getBytes(); int buffSize = _redzone.length + uidString.length + imageSize + 8; // don't put in endOfLog since we keep overwriting that. RandomAccessFile ofile = null; java.nio.channels.FileLock lock = null; if (imageSize > 0) { TransactionData theLogEntry = getLogName(objUid, tName, buffSize); // always adds entry to log LogInstance theLog = theLogEntry.container; if (theLog == null) throw new ObjectStoreException(); fname = genPathName(theLog.getName(), tName, ft); fd = openAndLock(fname, FileLock.F_WRLCK, true); if (fd == null) { tsLogger.i18NLogger.warn_objectstore_ShadowingStore_18(fname); return false; } boolean setLength = !fd.exists(); try { ofile = new RandomAccessFile(fd, FILE_MODE); if (setLength) { ofile.setLength(_maxFileSize); } else { // may have to resize file if we keep updating this transaction info if (theLog.remaining() < buffSize) { long size = ofile.length() + buffSize - theLog.remaining(); ofile.setLength(size); theLog.resize(size); } } java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize); buff.put(_redzone); buff.putInt(uidString.length); buff.put(uidString); buff.putInt(imageSize); buff.put(state.buffer()); synchronized (_lock) { ofile.seek(theLogEntry.offset); ofile.write(buff.array()); } } catch (SyncFailedException e) { unlockAndClose(fd, ofile); throw new ObjectStoreException( "ShadowingStore::write_state() - write failed to sync for " + fname, e); } catch (FileNotFoundException e) { unlockAndClose(fd, ofile); e.printStackTrace(); throw new ObjectStoreException( "ShadowingStore::write_state() - write failed to locate file " + fname + ": " + e, e); } catch (IOException e) { unlockAndClose(fd, ofile); e.printStackTrace(); throw new ObjectStoreException( "ShadowingStore::write_state() - write failed for " + fname + ": " + e, e); } finally { try { if (lock != null) lock.release(); } catch (IOException ex) { ex.printStackTrace(); } } } if (!unlockAndClose(fd, ofile)) { tsLogger.i18NLogger.warn_objectstore_ShadowingStore_19(fname); } super.addToCache(fname); return true; } else throw new ObjectStoreException( "ShadowStore::write_state - " + tsLogger.i18NLogger.get_objectstore_notypenameuid() + objUid); }
/** * @author Mark Little ([email protected]) * @version $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $ * @since JTS 1.2.4. */ public class XAResourceRecord extends AbstractRecord { public static final int XACONNECTION = 0; private static final Uid START_XARESOURCE = Uid.minUid(); private static final Uid END_XARESOURCE = Uid.maxUid(); /** * The params represent specific parameters we need to recreate the connection to the database in * the event of a failure. If they're not set then recovery is out of our control. * * <p>Could also use it to pass other information, such as the readonly flag. */ public XAResourceRecord(TransactionImple tx, XAResource res, Xid xid, Object[] params) { super(new Uid(), null, ObjectType.ANDPERSISTENT); if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.XAResourceRecord ( " + xid + ", " + res + " )"); } _theXAResource = res; if (_xaResourceRecordWrappingPlugin != null) { _xaResourceRecordWrappingPlugin.transcribeWrapperData(this); } _recoveryObject = null; _tranID = xid; _valid = true; if (params != null) { if (params.length >= XACONNECTION) { if (params[XACONNECTION] instanceof RecoverableXAConnection) _recoveryObject = (RecoverableXAConnection) params[XACONNECTION]; } } _prepared = false; _heuristic = TwoPhaseOutcome.FINISH_OK; _theTransaction = tx; } public final Xid getXid() { return _tranID; } public Uid order() { if (_theXAResource instanceof StartXAResource) return START_XARESOURCE; else { if (_theXAResource instanceof EndXAResource) return END_XARESOURCE; else return super.order(); } } public boolean propagateOnCommit() { return false; // cannot ever be nested! } public int typeIs() { return RecordType.JTA_RECORD; } public Object value() { return _theXAResource; } public void setValue(Object o) { jtaLogger.i18NLogger.warn_resources_arjunacore_setvalue("XAResourceRecord::set_value()"); } public int nestedAbort() { return TwoPhaseOutcome.FINISH_OK; } public int nestedCommit() { return TwoPhaseOutcome.FINISH_OK; } /* * XA is not subtransaction aware. */ public int nestedPrepare() { return TwoPhaseOutcome.PREPARE_OK; // do nothing } public int topLevelPrepare() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.topLevelPrepare for " + this); } if (!_valid || (_theXAResource == null) || (_tranID == null)) { jtaLogger.i18NLogger.warn_resources_arjunacore_preparenulltx("XAResourceRecord.prepare"); removeConnection(); return TwoPhaseOutcome.PREPARE_NOTOK; } try { if (endAssociation()) { _theXAResource.end(_tranID, XAResource.TMSUCCESS); } _prepared = true; if (_theXAResource.prepare(_tranID) == XAResource.XA_RDONLY) { if (TxControl.isReadonlyOptimisation()) { // we won't be called again, so we need to tidy up now removeConnection(); } return TwoPhaseOutcome.PREPARE_READONLY; } else return TwoPhaseOutcome.PREPARE_OK; } catch (XAException e1) { jtaLogger.i18NLogger.warn_resources_arjunacore_preparefailed( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); /* * XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or * XAER_PROTO. */ if (_rollbackOptimization) // won't have rollback called on it removeConnection(); switch (e1.errorCode) { case XAException.XAER_RMERR: case XAException.XAER_RMFAIL: case XAException.XA_RBROLLBACK: case XAException.XA_RBEND: case XAException.XA_RBCOMMFAIL: case XAException.XA_RBDEADLOCK: case XAException.XA_RBINTEGRITY: case XAException.XA_RBOTHER: case XAException.XA_RBPROTO: case XAException.XA_RBTIMEOUT: case XAException.XAER_INVAL: case XAException.XAER_PROTO: case XAException .XAER_NOTA: // resource may have arbitrarily rolled back (shouldn't, but ...) return TwoPhaseOutcome.PREPARE_NOTOK; // will not call rollback default: return TwoPhaseOutcome .HEURISTIC_HAZARD; // we're not really sure (shouldn't get here though). } } catch (Exception e2) { jtaLogger.i18NLogger.warn_resources_arjunacore_preparefailed( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e2); if (_rollbackOptimization) // won't have rollback called on it removeConnection(); return TwoPhaseOutcome.PREPARE_NOTOK; } } public int topLevelAbort() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.topLevelAbort for " + this); } if (!_valid) return TwoPhaseOutcome.FINISH_ERROR; if (_theTransaction != null && _theTransaction.getXAResourceState(_theXAResource) == TxInfo.OPTIMIZED_ROLLBACK) { /* * Already rolledback during delist. */ return TwoPhaseOutcome.FINISH_OK; } if (_tranID == null) { jtaLogger.i18NLogger.warn_resources_arjunacore_rollbacknulltx("XAResourceRecord.rollback"); return TwoPhaseOutcome.FINISH_ERROR; } else { if (_theXAResource == null) _theXAResource = getNewXAResource(); if (_theXAResource != null) { if (_heuristic != TwoPhaseOutcome.FINISH_OK) return _heuristic; try { if (!_prepared) { if (endAssociation()) { _theXAResource.end(_tranID, XAResource.TMSUCCESS); } } } catch (XAException e1) { if ((e1.errorCode >= XAException.XA_RBBASE) && (e1.errorCode < XAException.XA_RBEND)) { /* * Has been marked as rollback-only. We still * need to call rollback. */ } else { jtaLogger.i18NLogger.warn_resources_arjunacore_rollbackerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); removeConnection(); return TwoPhaseOutcome.FINISH_ERROR; } } catch (RuntimeException e) { jtaLogger.i18NLogger.warn_resources_arjunacore_rollbackerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e); throw e; } try { _theXAResource.rollback(_tranID); } catch (XAException e1) { if (notAProblem(e1, false)) { // some other thread got there first (probably) } else { jtaLogger.i18NLogger.warn_resources_arjunacore_rollbackerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); switch (e1.errorCode) { case XAException.XAER_RMERR: if (!_prepared) break; // just do the finally block case XAException.XA_HEURHAZ: return TwoPhaseOutcome.HEURISTIC_HAZARD; case XAException.XA_HEURCOM: return TwoPhaseOutcome.HEURISTIC_COMMIT; case XAException.XA_HEURMIX: return TwoPhaseOutcome.HEURISTIC_MIXED; case XAException.XAER_NOTA: if (_recovered) break; // rolled back previously and recovery completed case XAException.XA_HEURRB: // forget? case XAException.XA_RBROLLBACK: case XAException.XA_RBEND: case XAException.XA_RBCOMMFAIL: case XAException.XA_RBDEADLOCK: case XAException.XA_RBINTEGRITY: case XAException.XA_RBOTHER: case XAException.XA_RBPROTO: case XAException.XA_RBTIMEOUT: break; default: return TwoPhaseOutcome.FINISH_ERROR; } } } catch (Exception e2) { jtaLogger.i18NLogger.warn_resources_arjunacore_rollbackerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e2); return TwoPhaseOutcome.FINISH_ERROR; } finally { if (!_prepared) removeConnection(); } } else { jtaLogger.i18NLogger.warn_resources_arjunacore_noresource(XAHelper.xidToString(_tranID)); if (XAResourceRecord._assumedComplete) { jtaLogger.i18NLogger.info_resources_arjunacore_assumecomplete( XAHelper.xidToString(_tranID)); return TwoPhaseOutcome.FINISH_OK; } else return TwoPhaseOutcome.FINISH_ERROR; } } return TwoPhaseOutcome.FINISH_OK; } public int topLevelCommit() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.topLevelCommit for " + this); } if (!_prepared) return TwoPhaseOutcome.NOT_PREPARED; if (_tranID == null) { jtaLogger.i18NLogger.warn_resources_arjunacore_commitnulltx("XAResourceRecord.commit"); return TwoPhaseOutcome.FINISH_ERROR; } else { if (_theXAResource == null) _theXAResource = getNewXAResource(); if (_theXAResource != null) { if (_heuristic != TwoPhaseOutcome.FINISH_OK) return _heuristic; /* * No need for end call here since we can only get to this * point by going through prepare. */ try { _theXAResource.commit(_tranID, false); } catch (XAException e1) { if (notAProblem(e1, true)) { // some other thread got there first (probably) } else { jtaLogger.i18NLogger.warn_resources_arjunacore_commitxaerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); /* * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or * XAER_PROTO. */ switch (e1.errorCode) { case XAException.XA_HEURHAZ: return TwoPhaseOutcome.HEURISTIC_HAZARD; case XAException.XA_HEURCOM: // what about forget? // OTS doesn't support // this code here. break; case XAException.XA_HEURRB: case XAException .XA_RBROLLBACK: // could really do with an ABORTED status in TwoPhaseOutcome to // differentiate case XAException.XA_RBCOMMFAIL: case XAException.XA_RBDEADLOCK: case XAException.XA_RBINTEGRITY: case XAException.XA_RBOTHER: case XAException.XA_RBPROTO: case XAException.XA_RBTIMEOUT: case XAException.XA_RBTRANSIENT: case XAException.XAER_RMERR: case XAException.XAER_PROTO: // XA spec implies rollback return TwoPhaseOutcome.HEURISTIC_ROLLBACK; case XAException.XA_HEURMIX: return TwoPhaseOutcome.HEURISTIC_MIXED; case XAException.XAER_NOTA: if (_recovered) break; // committed previously and recovery completed else return TwoPhaseOutcome.HEURISTIC_HAZARD; // something terminated the transaction! case XAException.XA_RETRY: case XAException.XAER_RMFAIL: _committed = true; // will cause log to be rewritten /* * Could do timeout retry here, but that could cause other resources in the list to go down the * heuristic path (some are far too keen to do this). Fail and let recovery retry. Meanwhile * the coordinator will continue to commit the other resources immediately. */ return TwoPhaseOutcome.FINISH_ERROR; case XAException.XAER_INVAL: // resource manager failed, did it rollback? default: return TwoPhaseOutcome.HEURISTIC_HAZARD; } } } catch (Exception e2) { jtaLogger.i18NLogger.warn_resources_arjunacore_commitxaerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e2); return TwoPhaseOutcome.FINISH_ERROR; } finally { removeConnection(); } } else { jtaLogger.i18NLogger.warn_resources_arjunacore_noresource(XAHelper.xidToString(_tranID)); if (XAResourceRecord._assumedComplete) { jtaLogger.i18NLogger.info_resources_arjunacore_assumecomplete( XAHelper.xidToString(_tranID)); return TwoPhaseOutcome.FINISH_OK; } else return TwoPhaseOutcome.FINISH_ERROR; } } return TwoPhaseOutcome.FINISH_OK; } /** * Is the XAException a non-error when received in reply to commit or rollback ? It normally is, * but may be overridden in recovery. */ protected boolean notAProblem(XAException ex, boolean commit) { return XAResourceErrorHandler.notAProblem(_theXAResource, ex, commit); } public int nestedOnePhaseCommit() { return TwoPhaseOutcome.FINISH_ERROR; } /** * For commit_one_phase we can do whatever we want since the transaction outcome is whatever we * want. Therefore, we do not need to save any additional recoverable state, such as a reference * to the transaction coordinator, since it will not have an intentions list anyway. */ public int topLevelOnePhaseCommit() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.topLevelOnePhaseCommit for " + this); } boolean commit = true; if (_tranID == null) { jtaLogger.i18NLogger.warn_resources_arjunacore_opcnulltx("XAResourceRecord.1pc"); return TwoPhaseOutcome.ONE_PHASE_ERROR; // rolled back!! } else { if (_theXAResource != null) { if (_heuristic != TwoPhaseOutcome.FINISH_OK) return _heuristic; XAException endHeuristic = null; XAException endRBOnly = null; try { /* * TODO in Oracle the end is not needed. Is this common * across other RMs? */ if (endAssociation()) { _theXAResource.end(_tranID, XAResource.TMSUCCESS); } } catch (XAException e1) { /* * Now it's not legal to return a heuristic from end, but * apparently Oracle does (http://jira.jboss.com/jira/browse/JBTM-343) * Since this is 1PC we can call forget: the outcome of the * transaction is the outcome of the participant. */ switch (e1.errorCode) { case XAException.XA_HEURHAZ: case XAException.XA_HEURMIX: case XAException.XA_HEURCOM: case XAException.XA_HEURRB: endHeuristic = e1; break; case XAException.XA_RBROLLBACK: case XAException.XA_RBCOMMFAIL: case XAException.XA_RBDEADLOCK: case XAException.XA_RBINTEGRITY: case XAException.XA_RBOTHER: case XAException.XA_RBPROTO: case XAException.XA_RBTIMEOUT: case XAException.XA_RBTRANSIENT: /* * Has been marked as rollback-only. We still * need to call rollback. */ endRBOnly = e1; commit = false; break; case XAException.XAER_RMERR: case XAException.XAER_NOTA: case XAException.XAER_PROTO: case XAException.XAER_INVAL: case XAException.XAER_RMFAIL: default: { jtaLogger.i18NLogger.warn_resources_arjunacore_opcerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); removeConnection(); return TwoPhaseOutcome.FINISH_ERROR; } } } catch (RuntimeException e) { jtaLogger.i18NLogger.warn_resources_arjunacore_opcerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e); throw e; } try { /* * Not strictly necessary since calling commit will * do the rollback if end failed as above. */ if (endHeuristic != null) // catch those RMs that terminate in end rather than follow the spec throw endHeuristic; if (commit) _theXAResource.commit(_tranID, true); else { _theXAResource.rollback(_tranID); throw endRBOnly; } } catch (XAException e1) { jtaLogger.i18NLogger.warn_resources_arjunacore_opcerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), XAHelper.printXAErrorCode(e1), e1); /* * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or * XAER_PROTO. XA_RB* */ switch (e1.errorCode) { case XAException.XA_HEURHAZ: case XAException.XA_HEURMIX: return TwoPhaseOutcome.HEURISTIC_HAZARD; case XAException.XA_HEURCOM: forget(); break; case XAException.XA_HEURRB: forget(); return TwoPhaseOutcome.ONE_PHASE_ERROR; case XAException.XA_RBROLLBACK: case XAException.XA_RBCOMMFAIL: case XAException.XA_RBDEADLOCK: case XAException.XA_RBINTEGRITY: case XAException.XA_RBOTHER: case XAException.XA_RBPROTO: case XAException.XA_RBTIMEOUT: case XAException.XA_RBTRANSIENT: case XAException.XAER_RMERR: return TwoPhaseOutcome.ONE_PHASE_ERROR; case XAException.XAER_NOTA: return TwoPhaseOutcome .HEURISTIC_HAZARD; // something committed or rolled back without asking us! case XAException.XAER_INVAL: // resource manager failed, did it rollback? return TwoPhaseOutcome.HEURISTIC_HAZARD; case XAException.XA_RETRY: // XA does not allow this to be thrown for 1PC! case XAException.XAER_PROTO: return TwoPhaseOutcome.ONE_PHASE_ERROR; // assume rollback case XAException.XAER_RMFAIL: default: _committed = true; // will cause log to be rewritten return TwoPhaseOutcome.FINISH_ERROR; // recovery should retry } } catch (Exception e2) { jtaLogger.i18NLogger.warn_resources_arjunacore_opcerror( XAHelper.xidToString(_tranID), _theXAResource.toString(), "-", e2); return TwoPhaseOutcome.FINISH_ERROR; } finally { removeConnection(); } } else return TwoPhaseOutcome.ONE_PHASE_ERROR; } if (commit) return TwoPhaseOutcome.FINISH_OK; else return TwoPhaseOutcome.FINISH_ERROR; } public boolean forgetHeuristic() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.forget for " + this); } forget(); removeConnection(); return true; } private void forget() { if ((_theXAResource != null) && (_tranID != null)) { _heuristic = TwoPhaseOutcome.FINISH_OK; try { _theXAResource.forget(_tranID); } catch (Exception e) { } } } /* * Independant recovery cannot occur. Must be driven by the recovery of the * local transaction, i.e., top-down recovery. */ protected int recover() { if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.recover"); } if (_committed) { /* * A previous commit attempt failed, but we know the intention * was to commit. So let's try again. */ if (topLevelCommit() == TwoPhaseOutcome.FINISH_OK) return XARecoveryResource.RECOVERED_OK; else return XARecoveryResource.FAILED_TO_RECOVER; } else return XARecoveryResource.WAITING_FOR_RECOVERY; } public boolean save_state(OutputObjectState os, int t) { boolean res = false; try { os.packInt(_heuristic); os.packBoolean(_committed); /* * Since we don't know what type of Xid we are using, leave it up to * XID to pack. */ XidImple.pack(os, _tranID); /* * If no recovery object set then rely upon object serialisation! */ if (_recoveryObject == null) { os.packInt(RecoverableXAConnection.OBJECT_RECOVERY); os.packString(_productName); os.packString(_productVersion); os.packString(_jndiName); if (_theXAResource instanceof Serializable) { try { ByteArrayOutputStream s = new ByteArrayOutputStream(); ObjectOutputStream o = new ObjectOutputStream(s); o.writeObject(_theXAResource); o.close(); os.packBoolean(true); String name = _theXAResource.getClass().getName(); os.packString(name); os.packBytes(s.toByteArray()); } catch (NotSerializableException ex) { jtaLogger.i18NLogger.warn_resources_arjunacore_savestate(); return false; } } else { // have to rely upon XAResource.recover! os.packBoolean(false); } } else { os.packInt(RecoverableXAConnection.AUTO_RECOVERY); os.packString(_recoveryObject.getClass().getName()); _recoveryObject.packInto(os); } res = true; } catch (Exception e) { jtaLogger.i18NLogger.warn_resources_arjunacore_savestateerror( _theXAResource.toString(), XAHelper.xidToString(_tranID), e); res = false; } if (res) res = super.save_state(os, t); return res; } public boolean restore_state(InputObjectState os, int t) { boolean res = false; try { _heuristic = os.unpackInt(); _committed = os.unpackBoolean(); _tranID = XidImple.unpack(os); _theXAResource = null; _recoveryObject = null; if (os.unpackInt() == RecoverableXAConnection.OBJECT_RECOVERY) { _productName = os.unpackString(); _productVersion = os.unpackString(); _jndiName = os.unpackString(); boolean haveXAResource = os.unpackBoolean(); if (haveXAResource) { try { // Read the classname of the serialized XAResource String className = os.unpackString(); byte[] b = os.unpackBytes(); ByteArrayInputStream s = new ByteArrayInputStream(b); ObjectInputStream o = new ObjectInputStream(s); // Give the list of deserializers a chance to deserialize the record boolean deserialized = false; Iterator<SerializableXAResourceDeserializer> iterator = getXAResourceDeserializers().iterator(); while (iterator.hasNext()) { SerializableXAResourceDeserializer proxyXAResourceDeserializer = iterator.next(); if (proxyXAResourceDeserializer.canDeserialze(className)) { _theXAResource = proxyXAResourceDeserializer.deserialze(o); deserialized = true; break; } } // Give it a go ourselves if (!deserialized) { _theXAResource = (XAResource) o.readObject(); } o.close(); if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace("XAResourceRecord.restore_state - XAResource de-serialized"); } } catch (Exception ex) { // not serializable in the first place! jtaLogger.i18NLogger.warn_resources_arjunacore_restorestate(ex); return false; } } else { /* * Lookup new XAResource via XARecoveryModule if possible. */ _theXAResource = getNewXAResource(); if (_theXAResource == null) { jtaLogger.i18NLogger.warn_resources_arjunacore_norecoveryxa(toString()); /* * Don't prevent tx from activating because there may be * other participants that can still recover. Plus, we will * try to get a new XAResource later for this instance. */ res = true; } } } else { String creatorName = os.unpackString(); _recoveryObject = ClassloadingUtility.loadAndInstantiateClass( RecoverableXAConnection.class, creatorName, null); if (_recoveryObject == null) { throw new ClassNotFoundException(); } _recoveryObject.unpackFrom(os); _theXAResource = _recoveryObject.getResource(); if (jtaLogger.logger.isTraceEnabled()) { jtaLogger.logger.trace( "XAResourceRecord.restore_state - XAResource got from " + creatorName); } } res = true; } catch (Exception e) { jtaLogger.i18NLogger.warn_resources_arjunacore_restorestateerror( _theXAResource.toString(), XAHelper.xidToString(_tranID), e); res = false; } if (res) res = super.restore_state(os, t); return res; } public String type() { return XAResourceRecord.typeName(); } public static String typeName() { return "/StateManager/AbstractRecord/XAResourceRecord"; } public boolean doSave() { return true; } public void merge(AbstractRecord a) {} public void alter(AbstractRecord a) {} public boolean shouldAdd(AbstractRecord a) { return false; } public boolean shouldAlter(AbstractRecord a) { return false; } public boolean shouldMerge(AbstractRecord a) { return false; } public boolean shouldReplace(AbstractRecord a) { return false; } /** * Returns the resource manager product name. * * @return the product name */ public String getProductName() { return _productName; } /** * Sets the resource manager product name. * * @param productName the product name */ public void setProductName(String productName) { this._productName = productName; } /** * Returns the resource manager product version. * * @return the product version */ public String getProductVersion() { return _productVersion; } /** * Sets the resource manager product version. * * @param productVersion the product version */ public void setProductVersion(String productVersion) { this._productVersion = productVersion; } /** * Returns the resource manager JNDI name for e.g. xa datasource. Note this is not used for * lookup, only for information. * * @return the JNDI name. */ public String getJndiName() { return _jndiName; } /** * Sets the resource manager JNDI name. Note this is not used for lookup, only for information. * * @param jndiName the JNDI name. */ public void setJndiName(String jndiName) { this._jndiName = jndiName; } public XAResourceRecord() { super(); _theXAResource = null; _recoveryObject = null; _tranID = null; _prepared = true; _heuristic = TwoPhaseOutcome.FINISH_OK; _valid = true; _theTransaction = null; _recovered = true; } public XAResourceRecord(Uid u) { super(u, null, ObjectType.ANDPERSISTENT); _theXAResource = null; _recoveryObject = null; _tranID = null; _prepared = true; _heuristic = TwoPhaseOutcome.FINISH_OK; _valid = true; _theTransaction = null; _recovered = true; } public String toString() { return "XAResourceRecord < resource:" + _theXAResource + ", txid:" + _tranID + ", heuristic: " + TwoPhaseOutcome.stringForm(_heuristic) + ((_productName != null && _productVersion != null) ? ", product: " + _productName + "/" + _productVersion : "") + ((_jndiName != null) ? ", jndiName: " + _jndiName : "") + " " + super.toString() + " >"; } private List<SerializableXAResourceDeserializer> getXAResourceDeserializers() { if (serializableXAResourceDeserializers != null) { return serializableXAResourceDeserializers; } synchronized (this) { if (serializableXAResourceDeserializers != null) { return serializableXAResourceDeserializers; } serializableXAResourceDeserializers = new ArrayList<SerializableXAResourceDeserializer>(); for (RecoveryModule recoveryModule : RecoveryManager.manager().getModules()) { if (recoveryModule instanceof XARecoveryModule) { XARecoveryModule xaRecoveryModule = (XARecoveryModule) recoveryModule; serializableXAResourceDeserializers.addAll( xaRecoveryModule.getSeriablizableXAResourceDeserializers()); return serializableXAResourceDeserializers; } } } return serializableXAResourceDeserializers; } /** * This routine finds the new XAResource for the transaction that used the old resource we * couldn't serialize. It does this by looking up the XARecoveryModule in the recovery manager and * asking it for the XAResource. The recovery manager will then look through its list of * registered XARecoveryResource implementations for the appropriate instance. If the * XARecoveryModule hasn't been initialised yet then this routine will fail, but on the next scan * it should work. */ private final XAResource getNewXAResource() { RecoveryManager recMan = RecoveryManager.manager(); Vector recoveryModules = recMan.getModules(); if (recoveryModules != null) { Enumeration modules = recoveryModules.elements(); while (modules.hasMoreElements()) { RecoveryModule m = (RecoveryModule) modules.nextElement(); if (m instanceof XARecoveryModule) { /* * Blaargh! There are better ways to do this! */ return ((XARecoveryModule) m).getNewXAResource(this); } } } return null; } private final void removeConnection() { /* * Should only be called once. Remove the connection so that user can * reuse the driver as though it were fresh (e.g., can do read only * optimisation). */ if (_recoveryObject != null) { _recoveryObject.close(); _recoveryObject = null; } if (_theTransaction != null) _theTransaction = null; } /* * Ask the transaction whether or not this XAResource is still associated * with the thread, i.e., has end already been called on it? */ private final boolean endAssociation() { boolean doEnd = true; if (_theTransaction != null) { int txInfo = _theTransaction.getXAResourceState(_theXAResource); if ((txInfo == TxInfo.NOT_ASSOCIATED) || (txInfo == TxInfo.FAILED)) { // end has been called so we don't need to do it again! doEnd = false; } } else doEnd = false; // Recovery mode return doEnd; } protected XAResource _theXAResource; private RecoverableXAConnection _recoveryObject; private Xid _tranID; private boolean _prepared; private boolean _valid; private int _heuristic; private boolean _committed = false; // try to optimize recovery private TransactionImple _theTransaction; private boolean _recovered = false; // extra metadata from the wrapper, if present private String _productName; private String _productVersion; private String _jndiName; private static final XAResourceRecordWrappingPlugin _xaResourceRecordWrappingPlugin = jtaPropertyManager.getJTAEnvironmentBean().getXAResourceRecordWrappingPlugin(); private static final boolean _rollbackOptimization = jtaPropertyManager.getJTAEnvironmentBean().isXaRollbackOptimization(); /* * WARNING: USE WITH EXTEREME CARE!! * * This assumes that if there is no XAResource that can deal with an Xid * after recovery, then we failed after successfully committing the transaction * but before updating the log. In which case we just need to ignore this * resource and remove the entry from the log. * * BUT if not all XAResourceRecovery instances are correctly implemented * (or present) we may end up removing participants that have not been dealt * with. Hence USE WITH EXTREME CARE!! */ private static final boolean _assumedComplete = jtaPropertyManager.getJTAEnvironmentBean().isXaAssumeRecoveryComplete(); private List<SerializableXAResourceDeserializer> serializableXAResourceDeserializers; }