/*
* 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.TransactionNotActiveException;
import com.sun.sgs.impl.util.WeakIdentityMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Stores information about managed references within a particular transaction.
* This class is logically part of the ManagedReferenceImpl class.
*/
final class ReferenceTable {
/**
* A map whose keys are stale managed objects, if tracking stale objects.
*/
private static final WeakIdentityMap<Object, Boolean>
staleObjects = new WeakIdentityMap<Object, Boolean>();
/** Maps object IDs to managed references. */
private final SortedMap<Long, ManagedReferenceImpl<?>> oids =
new TreeMap<Long, ManagedReferenceImpl<?>>();
/**
* Maps managed objects to managed references. The objects are compared by
* identity, not the equals method.
*/
private final Map<ManagedObject, ManagedReferenceImpl<?>> objects =
new IdentityHashMap<ManagedObject, ManagedReferenceImpl<?>>();
/** Whether to track stale objects. */
private final boolean trackStaleObjects;
/**
* Creates an instance of this class.
*
* @param trackStaleObjects whether to track stale objects
*/
ReferenceTable(boolean trackStaleObjects) {
this.trackStaleObjects = trackStaleObjects;
}
/**
* Finds the managed reference associated with a managed object, returning
* null if no reference is found.
*/
ManagedReferenceImpl<?> find(ManagedObject object) {
assert object != null : "Object is null";
ManagedReferenceImpl<?> result = objects.get(object);
if (result == null &&
trackStaleObjects &&
staleObjects.containsKey(object))
{
throw new TransactionNotActiveException(
"Attempt to access an object of type " +
DataServiceImpl.typeName(object) +
" whose transaction is no longer active");
}
return result;
}
/**
* Finds the managed reference associated with an object ID, returning null
* if no reference is found.
*/
ManagedReferenceImpl<?> find(long oid) {
assert oid >= 0 : "Object ID is negative";
return oids.get(oid);
}
/** Adds a new managed reference to this table. */
void add(ManagedReferenceImpl<?> ref) {
assert !oids.containsKey(ref.oid)
: "Found existing reference for oid:" + ref.oid;
oids.put(ref.oid, ref);
ManagedObject object = ref.getObject();
if (object != null) {
assert !objects.containsKey(object)
: "Found existing reference for object with oid:" + ref.oid;
objects.put(object, ref);
}
}
/**
* Updates this table for a reference that has been newly associated with
* an object.
*/
void registerObject(ManagedReferenceImpl<?> ref) {
assert oids.get(ref.oid) == ref
: "Found duplicate references for oid: " + ref.oid;
assert ref.getObject() != null : "Object is null for oid:" + ref.oid;
assert !objects.containsKey(ref.getObject())
: "Found existing reference for object with oid: " + ref.oid;
objects.put(ref.getObject(), ref);
}
/**
* Updates this table for a reference that is no longer associated with an
* object.
*/
void unregisterObject(ManagedObject object) {
assert objects.containsKey(object) : "Object was not found";
objects.remove(object);
if (trackStaleObjects) {
staleObjects.put(object, Boolean.TRUE);
}
}
/** Removes a managed reference from this table. */
void remove(ManagedReferenceImpl<?> ref) {
Object existing = oids.remove(ref.oid);
assert existing == ref
: "Found duplicate reference for oid:" + ref.oid;
ManagedObject object = ref.getObject();
if (object != null) {
existing = objects.remove(object);
assert existing == ref
: "Found duplicate reference for oid:" + ref.oid;
}
}
/**
* Returns the next object ID in the reference table of a newly created
* object, or -1 if none is found. Does not return IDs for removed
* objects. Specifying -1 requests the first ID.
*/
long nextNewObjectId(long oid) {
for (Entry<Long, ManagedReferenceImpl<?>> entry :
oids.tailMap(oid).entrySet())
{
long key = entry.getKey();
if (key > oid && entry.getValue().isNew()) {
return key;
}
}
return -1;
}
/**
* Flushes all references. Returns information about any objects found to
* be modified, or null if none were modified.
*/
FlushInfo flushModifiedObjects() {
FlushInfo flushInfo = null;
for (ManagedReferenceImpl<?> ref : oids.values()) {
byte[] data = ref.flush();
if (data != null) {
if (flushInfo == null) {
flushInfo = new FlushInfo();
}
flushInfo.add(ref.oid, data);
}
}
return flushInfo;
}
/**
* Checks the consistency of this table, throwing an assertion error if a
* problem is found.
*/
void checkAllState() {
int objectCount = 0;
for (Entry<Long, ManagedReferenceImpl<?>> entry : oids.entrySet()) {
long oid = entry.getKey();
ManagedReferenceImpl<?> ref = entry.getValue();
ref.checkState();
if (oid != ref.oid) {
throw new AssertionError(
"Wrong oids entry: oid = " + oid + ", ref.oid = " +
ref.oid);
}
Object object = ref.getObject();
if (object != null) {
ManagedReferenceImpl<?> objectsRef = objects.get(object);
if (objectsRef == null) {
throw new AssertionError(
"Missing objects entry for oid = " + ref.oid);
} else if (!ref.equals(objectsRef)) {
throw new AssertionError(
"Wrong objects entry for oid = " + ref.oid);
}
objectCount++;
}
}
if (objectCount != objects.size()) {
throw new AssertionError(
"Objects table has wrong size: was " + objects.size() +
", expected " + objectCount);
}
}
}