/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server 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.sun.sgs.impl.service.data; import com.sun.sgs.app.ManagedObject; import com.sun.sgs.app.ManagedObjectRemoval; import com.sun.sgs.impl.sharedutil.LoggerWrapper; import com.sun.sgs.impl.util.TransactionContext; import com.sun.sgs.service.Transaction; import com.sun.sgs.service.TransactionListener; import com.sun.sgs.service.store.DataStore; import java.math.BigInteger; import java.util.IdentityHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** Stores information for a specific transaction. */ final class Context extends TransactionContext implements TransactionListener { /** The logger for the data service class. */ private static final LoggerWrapper logger = new LoggerWrapper(Logger.getLogger(DataServiceImpl.class.getName())); /** The data service. */ private final DataServiceImpl service; /** The data store. */ final DataStore store; /** The transaction. */ final Transaction txn; /** * The number of operations to skip between checks of the consistency of * the reference table. */ private final int debugCheckInterval; /** Whether to detect modifications. */ final boolean detectModifications; /** Controls serializing classes. */ final ClassSerialization classSerial; /** * The number of operations performed -- used to determine when to make * checks on the reference table. */ private int count = 0; /** * Stores information about managed references. This field is logically * part of the ManagedReferenceImpl class. */ final ReferenceTable refs; /** * A map that records all managed objects that are currently having * ManagedObjectRemoval.removingObject called on them, to detect recursion, * or null. Uses identity comparison to avoid confusion by value-based * equals methods. */ private IdentityHashMap<ManagedObjectRemoval, Boolean> removing = null; /** Creates an instance of this class. */ Context(DataServiceImpl service, DataStore store, Transaction txn, int debugCheckInterval, boolean detectModifications, ClassesTable classesTable, boolean trackStaleObjects) { super(txn); assert service != null && store != null && txn != null && classesTable != null; this.service = service; this.store = store; this.txn = txn; this.debugCheckInterval = debugCheckInterval; this.detectModifications = detectModifications; refs = new ReferenceTable(trackStaleObjects); classSerial = classesTable.createClassSerialization(this.txn); txn.registerListener(this); if (logger.isLoggable(Level.FINER)) { logger.log(Level.FINER, "join tid:{0,number,#}, thread:{1}", getTxnId(), Thread.currentThread().getName()); } } /* -- Methods for obtaining references -- */ /** Obtains the reference associated with the specified object. */ <T> ManagedReferenceImpl<T> getReference(T object) { return ManagedReferenceImpl.getReference(this, object); } /** * Finds the existing reference associated with the specified object, * returning null if it is not found. Throws ObjectNotFoundException if * the object has been removed. */ <T> ManagedReferenceImpl<T> findReference(T object) { return ManagedReferenceImpl.findReference(this, object); } /** * Finds the existing reference associated with the specified object, * returning null if it is not found or has been removed. */ <T> ManagedReferenceImpl<T> safeFindReference(T object) { return ManagedReferenceImpl.safeFindReference(this, object); } /** Obtains the reference associated with the specified ID. */ ManagedReferenceImpl<?> getReference(long oid) { return ManagedReferenceImpl.getReference(this, oid); } /* -- Methods for bindings -- */ /** Obtains the object associated with the specified internal name. */ ManagedObject getBinding(String internalName, boolean forUpdate) { long id = store.getBinding(txn, internalName); assert id >= 0 : "Object ID must not be negative"; ManagedObject result; if (forUpdate) { result = (ManagedObject) getReference(id).getForUpdate(false); } else { result = (ManagedObject) getReference(id).get(false); } store.setBindingDescription(txn, internalName, result); return result; } /** Sets the object associated with the specified internal name. */ void setBinding(String internalName, Object object) { store.setBindingDescription(txn, internalName, object); store.setBinding(txn, internalName, getReference(object).oid); } /** Removes the object associated with the specified internal name. */ void removeBinding(String internalName) { store.removeBinding(txn, internalName); } /** Returns the next bound name. */ String nextBoundName(String internalName) { return store.nextBoundName(txn, internalName); } /* -- Methods for object IDs -- */ /** * Returns the next object ID, or -1 if there are no more objects. Does * not return IDs for removed objects. Specifying -1 requests the first * ID. */ long nextObjectId(long oid) { return ManagedReferenceImpl.nextObjectId(this, oid); } /* -- Methods for TransactionContext -- */ @Override public boolean prepare() throws Exception { try { isPrepared = true; if (logger.isLoggable(Level.FINER)) { logger.log(Level.FINER, "prepare tid:{0,number,#} returns true"); } return true; } catch (Exception e) { if (logger.isLoggable(Level.FINER)) { logger.logThrow(Level.FINER, e, "prepare tid:{0,number,#} throws", getTxnId()); } throw e; } } @Override public void commit() { try { isCommitted = true; if (logger.isLoggable(Level.FINER)) { logger.log(Level.FINER, "commit tid:{0,number,#} returns", getTxnId()); } } catch (RuntimeException e) { if (logger.isLoggable(Level.FINER)) { logger.logThrow(Level.FINER, e, "commit tid:{0,number,#} throws", getTxnId()); } throw e; } } @Override public void prepareAndCommit() throws Exception { try { isCommitted = true; if (logger.isLoggable(Level.FINER)) { logger.log(Level.FINER, "prepareAndCommit tid:{0,number,#} returns", getTxnId()); } } catch (RuntimeException e) { if (logger.isLoggable(Level.FINER)) { logger.logThrow(Level.FINER, e, "prepareAndCommit tid:{0,number,#} throws", getTxnId()); } throw e; } } @Override public void abort(boolean retryable) { try { if (logger.isLoggable(Level.FINER)) { logger.log(Level.FINER, "abort tid:{0,number,#} returns", getTxnId()); } } catch (RuntimeException e) { if (logger.isLoggable(Level.FINER)) { logger.logThrow(Level.FINER, e, "abort tid:{0,number,#} throws", getTxnId()); } throw e; } } /* -- Implement TransactionListener -- */ /** * {@inheritDoc} <p> * * This implementation flushes managed references and marks the transaction * inactive so that we'll notice if other beforeCompletion methods attempt * to call the data service. */ public void beforeCompletion() { ManagedReferenceImpl.flushAll(this); } /** * {@inheritDoc} <p> * * This implementation does nothing. */ public void afterCompletion(boolean commit) { } /** {@inheritDoc} */ public String getTypeName() { return Context.class.getName(); } /* -- Other methods -- */ /** * Checks the consistency of the reference table if the operation count * equals the check interval. Throws an IllegalStateException if it * encounters a problem. */ void maybeCheckReferenceTable() { if (++count > debugCheckInterval) { count = 0; ManagedReferenceImpl.checkAllState(this); } } /** Checks that the service is running or shutting down. */ void checkState() { service.checkState(); } /** Calls removingObject on the argument, and checks for recursion. */ void removingObject(ManagedObjectRemoval object) { if (removing == null) { removing = new IdentityHashMap<ManagedObjectRemoval, Boolean>(); } if (removing.containsKey(object)) { throw new IllegalStateException( "Attempt to remove object recursively: " + object); } try { removing.put(object, Boolean.TRUE); object.removingObject(); } finally { removing.remove(object); } } /** Returns the ID of the associated transaction as a BigInteger. */ BigInteger getTxnId() { return new BigInteger(1, txn.getId()); } /** Returns whether to delay write locking until commit time. */ boolean optimisticWriteLocks() { return service.optimisticWriteLocks; } }