/* 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.foundation.*;
import com.db4o.internal.ids.*;
import com.db4o.internal.references.*;
import com.db4o.marshall.*;
import com.db4o.reflect.*;
/**
* @exclude
*/
public abstract class Transaction {
private Context _context;
// contains DeleteInfo nodes
Tree _delete;
protected final Transaction _systemTransaction;
/**
* This is the inside representation to operate against, the actual
* file-based ObjectContainerBase or the client. For all calls
* against this ObjectContainerBase the method signatures that take
* a transaction have to be used.
*/
private final ObjectContainerBase _container;
/**
* This is the outside representation to the user. This ObjectContainer
* should use this transaction as it's main user transation, so it also
* allows using the method signatures on ObjectContainer without a
* transaction.
*/
private ObjectContainer _objectContainer;
private List4 _transactionListeners;
private final ReferenceSystem _referenceSystem;
private final Map<TransactionLocal<?>, Object> _locals = new HashMap<TransactionLocal<?>, Object>();
public Transaction(ObjectContainerBase container, Transaction systemTransaction, ReferenceSystem referenceSystem) {
_container = container;
_systemTransaction = systemTransaction;
_referenceSystem = referenceSystem;
}
/**
* Retrieves the value of a transaction local variables.
*
* If this is the first time the variable is accessed {@link TransactionLocal#initialValueFor(Transaction)}
* will provide the initial value.
*/
public <T> ByRef<T> get(TransactionLocal<T> local) {
final ByRef<T> existing = (ByRef<T>) _locals.get(local);
if (null != existing)
return existing;
final ByRef<T> initialValue = ByRef.newInstance(local.initialValueFor(this));
_locals.put(local, initialValue);
return initialValue;
}
public final void checkSynchronization() {
if(Debug4.checkSychronization){
container().lock().notify();
}
}
public void addTransactionListener(TransactionListener listener) {
_transactionListeners = new List4(_transactionListeners, listener);
}
protected final void clearAll() {
clear();
_transactionListeners = null;
_locals.clear();
}
protected abstract void clear();
public void close(boolean rollbackOnClose) {
if (container() != null) {
checkSynchronization();
container().releaseSemaphores(this);
discardReferenceSystem();
}
if (rollbackOnClose) {
rollback();
}
TransactionalIdSystem idSystem = idSystem();
if(idSystem != null){
idSystem.close();
}
}
protected void discardReferenceSystem() {
if(_referenceSystem != null){
container().referenceSystemRegistry().removeReferenceSystem(_referenceSystem);
}
}
public abstract void commit();
protected void commitTransactionListeners() {
checkSynchronization();
if (_transactionListeners != null) {
Iterator4 i = new Iterator4Impl(_transactionListeners);
while (i.moveNext()) {
((TransactionListener) i.current()).preCommit();
}
_transactionListeners = null;
}
}
protected boolean isSystemTransaction() {
return _systemTransaction == null;
}
public boolean delete(ObjectReference ref, int id, int cascade) {
checkSynchronization();
if(ref != null){
if(! _container.flagForDelete(ref)){
return false;
}
}
if(DTrace.enabled){
DTrace.TRANS_DELETE.log(id);
}
DeleteInfo info = (DeleteInfo) TreeInt.find(_delete, id);
if(info == null){
info = new DeleteInfo(id, ref, cascade);
_delete = Tree.add(_delete, info);
return true;
}
info._reference = ref;
if(cascade > info._cascade){
info._cascade = cascade;
}
return true;
}
public void dontDelete(int a_id) {
if(DTrace.enabled){
DTrace.TRANS_DONT_DELETE.log(a_id);
}
if(_delete == null){
return;
}
_delete = TreeInt.removeLike((TreeInt)_delete, a_id);
}
public abstract void processDeletes();
public ReferenceSystem referenceSystem() {
if(_referenceSystem != null){
return _referenceSystem;
}
return parentTransaction().referenceSystem();
}
public final Reflector reflector(){
return container().reflector();
}
public abstract void rollback();
protected void rollBackTransactionListeners() {
checkSynchronization();
if (_transactionListeners != null) {
Iterator4 i = new Iterator4Impl(_transactionListeners);
while (i.moveNext()) {
((TransactionListener) i.current()).postRollback();
}
_transactionListeners = null;
}
}
boolean supportsVirtualFields(){
return true;
}
public Transaction systemTransaction(){
if(_systemTransaction != null){
return _systemTransaction;
}
return this;
}
public String toString() {
return container().toString();
}
public abstract void writeUpdateAdjustIndexes(int id, ClassMetadata clazz, ArrayType typeInfo);
public final ObjectContainerBase container() {
return _container;
}
public Transaction parentTransaction() {
return _systemTransaction;
}
public void rollbackReferenceSystem() {
referenceSystem().rollback();
}
public void postCommit(){
commitReferenceSystem();
}
public void commitReferenceSystem() {
referenceSystem().commit();
}
public void addNewReference(ObjectReference ref) {
referenceSystem().addNewReference(ref);
}
public final Object objectForIdFromCache(int id){
ObjectReference ref = referenceForId(id);
if (ref == null) {
return null;
}
Object candidate = ref.getObject();
if(candidate == null){
removeReference(ref);
}
return candidate;
}
public final ObjectReference referenceForId(int id) {
ObjectReference ref = referenceSystem().referenceForId(id);
if(ref != null){
if(ref.getObject() == null){
removeReference(ref);
return null;
}
return ref;
}
if(parentTransaction() != null){
return parentTransaction().referenceForId(id);
}
return null;
}
public final ObjectReference referenceForObject(Object obj) {
ObjectReference ref = referenceSystem().referenceForObject(obj);
if(ref != null){
return ref;
}
if(parentTransaction() != null){
return parentTransaction().referenceForObject(obj);
}
return null;
}
public final void removeReference(ObjectReference ref) {
referenceSystem().removeReference(ref);
// setting the ID to minus 1 ensures that the
// gc mechanism does not kill the new YapObject
ref.setID(-1);
Platform4.killYapRef(ref.getObjectReference());
}
public final void removeObjectFromReferenceSystem(Object obj){
ObjectReference ref = referenceForObject(obj);
if(ref != null){
removeReference(ref);
}
}
public void setOutSideRepresentation(ObjectContainer objectContainer){
_objectContainer = objectContainer;
}
public ObjectContainer objectContainer(){
if(_objectContainer != null){
return _objectContainer;
}
return _container;
}
public Context context(){
if(_context == null){
_context = new Context(){
public ObjectContainer objectContainer() {
return Transaction.this.objectContainer();
}
public Transaction transaction() {
return Transaction.this;
}
};
}
return _context;
}
protected void traverseDelete(Visitor4 deleteVisitor) {
if (_delete == null) {
return;
}
_delete.traverse(deleteVisitor);
_delete = null;
}
public Object wrap(Object value) {
if(value instanceof Integer){
return value;
}
return new TransactionContext(this, value);
}
public abstract TransactionalIdSystem idSystem();
public abstract long versionForId(int id);
public abstract long generateTransactionTimestamp(long forcedTimeStamp);
public abstract void useDefaultTransactionTimestamp();
public void postOpen(){
if(_systemTransaction != null){
_systemTransaction.postOpen();
}
}
}