/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal; import java.util.*; import com.db4o.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.activation.*; import com.db4o.internal.callbacks.*; import com.db4o.internal.ids.*; import com.db4o.internal.references.*; /** * @exclude */ public class LocalTransaction extends Transaction { private final IdentitySet4 _participants = new IdentitySet4(); Tree _writtenUpdateAdjustedIndexes; protected final LocalObjectContainer _file; private final CommittedCallbackDispatcher _committedCallbackDispatcher; private final TransactionalIdSystem _idSystem; private CommitTimestampSupport _commitTimestampSupport = null; private long _timestamp; private List<Long> _concurrentReplicationTimestamps; public LocalTransaction(ObjectContainerBase container, Transaction parentTransaction, TransactionalIdSystem idSystem, ReferenceSystem referenceSystem) { super(container, parentTransaction, referenceSystem); _file = (LocalObjectContainer) container; _committedCallbackDispatcher = new CommittedCallbackDispatcher() { public boolean willDispatchCommitted() { return callbacks().caresAboutCommitted(); } public void dispatchCommitted(CallbackObjectInfoCollections committedInfo) { callbacks().commitOnCompleted(LocalTransaction.this, committedInfo, false); } }; _idSystem = idSystem; } public Config4Impl config() { return container().config(); } public LocalObjectContainer localContainer() { return _file; } public void commit() { commit(_committedCallbackDispatcher); } public void commit(CommittedCallbackDispatcher dispatcher) { synchronized (container().lock()) { commitListeners(); dispatchCommittingCallback(); if (!doCommittedCallbacks(dispatcher)) { commitImpl(); commitClearAll(); } else { Collection4 deleted = collectCommittedCallbackDeletedInfo(); commitImpl(); final CallbackObjectInfoCollections committedInfo = collectCommittedCallbackInfo(deleted); commitClearAll(); dispatcher.dispatchCommitted( CallbackObjectInfoCollections.EMTPY == committedInfo ? committedInfo : new CallbackObjectInfoCollections( committedInfo.added, committedInfo.updated, new ObjectInfoCollectionImpl(deleted))); } } } private void dispatchCommittingCallback() { if(doCommittingCallbacks()){ callbacks().commitOnStarted(this, collectCommittingCallbackInfo()); } } private boolean doCommittedCallbacks(CommittedCallbackDispatcher dispatcher) { if (isSystemTransaction()){ return false; } return dispatcher.willDispatchCommitted(); } private boolean doCommittingCallbacks() { if (isSystemTransaction()) { return false; } return callbacks().caresAboutCommitting(); } public void enlist(TransactionParticipant participant) { if (null == participant) { throw new ArgumentNullException(); } checkSynchronization(); if (!_participants.contains(participant)) { _participants.add(participant); } } private void commitImpl(){ if(DTrace.enabled){ DTrace.TRANS_COMMIT.logInfo( "server == " + container().isServer() + ", systemtrans == " + isSystemTransaction()); } commitClassMetadata(); commitParticipants(); container().writeDirtyClassMetadata(); idSystem().commit(new FreespaceCommitter(localContainer().freespaceManager())); } private void commitListeners(){ commitParentListeners(); commitTransactionListeners(); } private void commitParentListeners() { if (_systemTransaction != null) { parentLocalTransaction().commitListeners(); } } private void commitParticipants() { if (parentLocalTransaction() != null) { parentLocalTransaction().commitParticipants(); } Iterator4 iterator = _participants.iterator(); while (iterator.moveNext()) { ((TransactionParticipant)iterator.current()).commit(this); } } private void commitClassMetadata(){ container().processPendingClassUpdates(); container().writeDirtyClassMetadata(); container().classCollection().write(container().systemTransaction()); } private LocalTransaction parentLocalTransaction() { return (LocalTransaction) _systemTransaction; } private void commitClearAll(){ if(_systemTransaction != null){ parentLocalTransaction().commitClearAll(); } clearAll(); } protected void clear() { idSystem().clear(); disposeParticipants(); _participants.clear(); } private void disposeParticipants() { Iterator4 iterator = _participants.valuesIterator(); while (iterator.moveNext()) { ((TransactionParticipant)iterator.current()).dispose(this); } } public void rollback() { synchronized (container().lock()) { rollbackParticipants(); idSystem().rollback(); rollBackTransactionListeners(); clearAll(); } } private void rollbackParticipants() { Iterator4 iterator = _participants.valuesIterator(); while (iterator.moveNext()) { ((TransactionParticipant)iterator.current()).rollback(this); } } public void flushFile(){ if(DTrace.enabled){ DTrace.TRANS_FLUSH.log(); } _file.syncFiles(); } public void processDeletes() { if (_delete == null) { _writtenUpdateAdjustedIndexes = null; return; } while (_delete != null) { Tree delete = _delete; _delete = null; delete.traverse(new Visitor4() { public void visit(Object a_object) { DeleteInfo info = (DeleteInfo) a_object; // if the object has been deleted if (localContainer().isDeleted(LocalTransaction.this, info._key)) { return; } // We need to hold a hard reference here, otherwise we can get // intermediate garbage collection kicking in. Object obj = null; if (info._reference != null) { obj = info._reference.getObject(); } if (obj == null || info._reference.getID() < 0) { // This means the object was gc'd. // Let's try to read it again, but this may fail in // CS mode if another transaction has deleted it. HardObjectReference hardRef = container().getHardObjectReferenceById( LocalTransaction.this, info._key); if(hardRef == HardObjectReference.INVALID){ return; } info._reference = hardRef._reference; info._reference.flagForDelete(container().topLevelCallId()); obj = info._reference.getObject(); } container().delete3(LocalTransaction.this, info._reference, obj, info._cascade, false); } }); } _writtenUpdateAdjustedIndexes = null; } public void writeUpdateAdjustIndexes(int id, ClassMetadata clazz, ArrayType typeInfo) { new WriteUpdateProcessor(this, id, clazz, typeInfo).run(); } private Callbacks callbacks(){ return container().callbacks(); } private Collection4 collectCommittedCallbackDeletedInfo() { final Collection4 deleted = new Collection4(); collectCallBackInfo(new CallbackInfoCollector() { public void deleted(int id) { ObjectInfo ref = frozenReferenceFor(id); if(ref != null){ deleted.add(ref); } } public void updated(int id) { } public void added(int id) { } }); return deleted; } private CallbackObjectInfoCollections collectCommittedCallbackInfo(Collection4 deleted) { if (! idSystem().isDirty()) { return CallbackObjectInfoCollections.EMTPY; } final Collection4 added = new Collection4(); final Collection4 updated = new Collection4(); collectCallBackInfo(new CallbackInfoCollector() { public void added(int id) { added.add(lazyReferenceFor(id)); } public void updated(int id) { updated.add(lazyReferenceFor(id)); } public void deleted(int id) { } }); return newCallbackObjectInfoCollections(added, updated, deleted); } private CallbackObjectInfoCollections collectCommittingCallbackInfo() { if (! idSystem().isDirty()) { return CallbackObjectInfoCollections.EMTPY; } final Collection4 added = new Collection4(); final Collection4 deleted = new Collection4(); final Collection4 updated = new Collection4(); collectCallBackInfo(new CallbackInfoCollector() { public void added(int id) { added.add(lazyReferenceFor(id)); } public void updated(int id) { updated.add(lazyReferenceFor(id)); } public void deleted(int id){ ObjectInfo ref = frozenReferenceFor(id); if(ref != null){ deleted.add(ref); } } }); return newCallbackObjectInfoCollections(added, updated, deleted); } private CallbackObjectInfoCollections newCallbackObjectInfoCollections( final Collection4 added, final Collection4 updated, final Collection4 deleted) { return new CallbackObjectInfoCollections( new ObjectInfoCollectionImpl(added), new ObjectInfoCollectionImpl(updated), new ObjectInfoCollectionImpl(deleted)); } private void collectCallBackInfo(final CallbackInfoCollector collector) { idSystem().collectCallBackInfo(collector); } public TransactionalIdSystem idSystem() { return _idSystem; } public ObjectInfo frozenReferenceFor(final int id) { ObjectReference ref = referenceForId(id); if(ref != null){ if (isStruct(ref)) return null; return new FrozenObjectInfo(this, ref, true); } ref = container().peekReference(systemTransaction(), id, new FixedActivationDepth(0), true); if(ref == null || ref.getObject() == null || isStruct(ref)){ return null; } return new FrozenObjectInfo(systemTransaction(), ref, true); } private boolean isStruct(ObjectReference ref) { return ref.classMetadata().isStruct(); } public LazyObjectReference lazyReferenceFor(final int id) { return new LazyObjectReference(LocalTransaction.this, id); } public long versionForId(int id) { return commitTimestampSupport().versionForId(id); } public CommitTimestampSupport commitTimestampSupport() { if (!isSystemTransaction()) { throw new IllegalStateException(); } if (_commitTimestampSupport == null) { _commitTimestampSupport = new CommitTimestampSupport(localContainer()); } return _commitTimestampSupport; } public long generateTransactionTimestamp(long forcedTimeStamp){ if(forcedTimeStamp > 0){ _timestamp = forcedTimeStamp; } else { _timestamp = localContainer().generateTimeStampId(); } return _timestamp; } public void useDefaultTransactionTimestamp(){ _timestamp = 0; _concurrentReplicationTimestamps = null; } public long timestamp(){ return _timestamp; } public void notifyAboutOtherReplicationCommit(long replicationVersion, List<Long> concurrentTimestamps) { if(timestamp() == 0){ return; } if(_concurrentReplicationTimestamps == null){ _concurrentReplicationTimestamps = new ArrayList<Long>(); } _concurrentReplicationTimestamps.add(replicationVersion); concurrentTimestamps.add(timestamp()); } public List<Long> concurrentReplicationTimestamps(){ if(_concurrentReplicationTimestamps != null){ return _concurrentReplicationTimestamps; } return new ArrayList<Long>(); } @Override public void postOpen(){ super.postOpen(); if (isSystemTransaction()) { commitTimestampSupport().ensureInitialized(); } } }