public void arrayChanged(TCObject source, int startPos, Object array, int length) { if (isTransactionLoggingDisabled()) { return; } try { disableTransactionLogging(); Object pojo = source.getPeerObject(); ClientTransaction tx = getTransaction(pojo); if (!ClassUtils.isPrimitiveArray(array)) { Object[] objArray = (Object[]) array; for (int i = 0; i < length; i++) { Object element = objArray[i]; if (!literalValues.isLiteralInstance(element)) { if (element != null) objectManager.checkPortabilityOfField(element, String.valueOf(i), pojo); TCObject tco = objectManager.lookupOrCreate(element); objArray[i] = tco.getObjectID(); // record the reference in this transaction -- This is to solve the race condition of // transactions // that reference objects newly "created" in other transactions that may not commit // before us if (element != null) tx.createObject(tco); } } } tx.arrayChanged(source, startPos, array, length); } finally { enableTransactionLogging(); } }
/** * In order to support ReentrantLock, the TransactionContext that is going to be removed when * doing a commit may not always be at the top of a stack because an reentrant lock could issue a * lock within a synchronized block (although it is highly not recommended). Therefore, when a * commit is issued, we need to search back the stack from the top of the stack to find the * appropriate TransactionContext to be removed. Most likely, the TransactionContext to be removed * will be on the top of the stack. Therefore, the performance should be make must difference. * Only in some weird situations where reentrantLock is mixed with synchronized block will the * TransactionContext to be removed be found otherwise. */ public void commit(String lockName) throws UnlockedSharedObjectException { logCommit0(); if (isTransactionLoggingDisabled() || objectManager.isCreationInProgress()) { return; } // ClientTransaction tx = popTransaction(); ClientTransaction tx = getTransaction(); LockID lockID = lockManager.lockIDFor(lockName); if (lockID == null || lockID.isNull()) { lockID = tx.getLockID(); } boolean hasCommitted = commit(lockID, tx, false); popTransaction(lockManager.lockIDFor(lockName)); if (peekContext() != null) { if (hasCommitted) { createTxAndInitContext(); } else { // If the current transaction has not committed, we will reuse the current transaction // so that the current changes will have a chance to commit at the next commit point. tx.setTransactionContext(peekContext()); setTransaction(tx); } } }
public boolean tryBegin(String lockName, WaitInvocation timeout, int lockLevel) { logTryBegin0(lockName, lockLevel); if (isTransactionLoggingDisabled() || objectManager.isCreationInProgress()) { return true; } final TxnType txnType = getTxnTypeFromLockLevel(lockLevel); ClientTransaction currentTransaction = getTransactionOrNull(); if ((currentTransaction != null) && lockLevel == LockLevel.CONCURRENT) { // make formatter sane throw new AssertionError("Can't acquire concurrent locks in a nested lock context."); } final LockID lockID = lockManager.lockIDFor(lockName); boolean isLocked = lockManager.tryLock(lockID, timeout, lockLevel); if (!isLocked) { return isLocked; } pushTxContext(lockID, txnType); if (currentTransaction == null) { createTxAndInitContext(); } else { currentTransaction.setTransactionContext(this.peekContext()); } return isLocked; }
// This can be overridden by subclass if you want different behavior. protected Object getObjectForKey(final ClientObjectManager objectManager, final Object k) throws ClassNotFoundException { try { return (k instanceof ObjectID ? objectManager.lookupObject((ObjectID) k) : k); } catch (AbortedOperationException e) { throw new TCRuntimeException(e); } }
// This can be overridden by subclass if you want different behavior. private Object getObjectForValue(final ClientObjectManager objectManager, final Object v) throws ClassNotFoundException { try { return (v instanceof ObjectID ? objectManager.lookupObject((ObjectID) v) : v); } catch (AbortedOperationException e) { throw new TCRuntimeException(e); } }
private boolean commitInternal( LockID lockID, ClientTransaction currentTransaction, boolean isWaitContext) { Assert.assertNotNull("transaction", currentTransaction); try { disableTransactionLogging(); // If the current transactionContext is READ_ONLY, there is no need to commit. TransactionContext tc = peekContext(lockID); if (tc.getType().equals(TxnType.READ_ONLY)) { txMonitor.committedReadTransaction(); return false; } boolean hasPendingCreateObjects = objectManager.hasPendingCreateObjects(); if (hasPendingCreateObjects) { objectManager.addPendingCreateObjectsToTransaction(); } currentTransaction.setAlreadyCommitted(); if (currentTransaction.hasChangesOrNotifies() || hasPendingCreateObjects) { if (txMonitor.isEnabled()) { currentTransaction.updateMBean(txMonitor); } remoteTxManager.commit(currentTransaction); } return true; } finally { enableTransactionLogging(); // always try to unlock even if we are throwing an exception // if (!isWaitContext && !currentTransaction.isNull()) { // lockManager.unlock(currentTransaction.getLockID()); // } if (!isWaitContext && !currentTransaction.isNull()) { if (lockID != null && !lockID.isNull()) { lockManager.unlock(lockID); } else { throw new AssertionError("Trying to unlock with lockID = null!"); } } } }
private void basicApply(Collection objectChanges, Map newRoots, boolean force) throws DNAException { List l = new LinkedList(); for (Iterator i = objectChanges.iterator(); i.hasNext(); ) { DNA dna = (DNA) i.next(); TCObject tcobj = null; Assert.assertTrue(dna.isDelta()); try { // This is a major hack to prevent distributed method calls // sent to apps that don't have the right classes from dying // This should be fixed in a better way some day :-) objectManager.getClassFor( Namespace.parseClassNameIfNecessary(dna.getTypeName()), dna.getDefiningLoaderDescription()); tcobj = objectManager.lookup(dna.getObjectID()); } catch (ClassNotFoundException cnfe) { logger.warn("Could not apply change because class not local:" + dna.getTypeName()); continue; } // Important to have a hard reference to the object while we apply // changes so that it doesn't get gc'd on us Object obj = tcobj == null ? null : tcobj.getPeerObject(); l.add(obj); if (obj != null) { try { tcobj.hydrate(dna, force); } catch (ClassNotFoundException cnfe) { logger.warn("Could not apply change because class not local:" + cnfe.getMessage()); throw new TCClassNotFoundException(cnfe); } } } for (Iterator i = newRoots.entrySet().iterator(); i.hasNext(); ) { Entry entry = (Entry) i.next(); String rootName = (String) entry.getKey(); ObjectID newRootID = (ObjectID) entry.getValue(); objectManager.replaceRootIDIfNecessary(rootName, newRootID); } }
public void logicalInvoke(TCObject source, int method, String methodName, Object[] parameters) { if (isTransactionLoggingDisabled()) { return; } try { disableTransactionLogging(); Object pojo = source.getPeerObject(); ClientTransaction tx = getTransaction(pojo); for (int i = 0; i < parameters.length; i++) { Object p = parameters[i]; boolean isLiteral = literalValues.isLiteralInstance(p); if (!isLiteral) { if (p != null) { objectManager.checkPortabilityOfLogicalAction(parameters, i, methodName, pojo); } TCObject tco = objectManager.lookupOrCreate(p); parameters[i] = tco.getObjectID(); if (p != null) { // record the reference in this transaction -- This is to solve the race condition of // transactions // that reference objects newly "created" in other transactions that may not commit // before us tx.createObject(tco); } } } tx.logicalInvoke(source, method, parameters, methodName); } finally { enableTransactionLogging(); } }
public void fieldChanged( TCObject source, String classname, String fieldname, Object newValue, int index) { if (isTransactionLoggingDisabled()) { return; } try { disableTransactionLogging(); Object pojo = source.getPeerObject(); ClientTransaction tx = getTransaction(pojo); logFieldChanged0(source, classname, fieldname, newValue, tx); if (newValue != null && literalValues.isLiteralInstance(newValue)) { tx.fieldChanged(source, classname, fieldname, newValue, index); } else { if (newValue != null) { objectManager.checkPortabilityOfField(newValue, fieldname, pojo); } TCObject tco = objectManager.lookupOrCreate(newValue); tx.fieldChanged(source, classname, fieldname, tco.getObjectID(), index); // record the reference in this transaction -- This is to solve the race condition of // transactions // that reference objects newly "created" in other transactions that may not commit before // us if (newValue != null) { tx.createObject(tco); } } } finally { enableTransactionLogging(); } }
protected void apply( final ClientObjectManager objectManager, final Object po, final LogicalOperation method, final Object[] params) throws ClassNotFoundException { final ToolkitMapImpl m = (ToolkitMapImpl) po; switch (method) { case PUT: final Object k = params[0]; final Object v = params[1]; final Object pkey = getObjectForKey(objectManager, k); final Object value = getObjectForValue(objectManager, v); m.internalPut(pkey, value); break; case REMOVE: Object rkey; try { rkey = params[0] instanceof ObjectID ? objectManager.lookupObject((ObjectID) params[0]) : params[0]; } catch (AbortedOperationException e) { throw new TCRuntimeException(e); } m.internalRemove(rkey); break; case CLEAR: m.internalClear(); break; case DESTROY: ((DestroyApplicator) m).applyDestroy(); break; default: throw new AssertionError("invalid action:" + method); } }
public boolean begin(String lockName, int lockLevel) { logBegin0(lockName, lockLevel); if (isTransactionLoggingDisabled() || objectManager.isCreationInProgress()) { return false; } final TxnType txnType = getTxnTypeFromLockLevel(lockLevel); ClientTransaction currentTransaction = getTransactionOrNull(); final LockID lockID = lockManager.lockIDFor(lockName); pushTxContext(lockID, txnType); if (currentTransaction == null) { createTxAndInitContext(); } else { currentTransaction.setTransactionContext(this.peekContext()); } lockManager.lock(lockID, lockLevel); return true; }