Permalink
Browse files

Fixed issue #111 and #112, partially reverting #75. This handles reat…

…taching detached objects.
  • Loading branch information...
tzaeschke committed Sep 17, 2018
1 parent c9d64ba commit 73d0d5f8b3df4228bd7ce3753cf8c4dec6dad64d
View
@@ -1,5 +1,10 @@
CHANGELOG
2018-Sep-17
===========
- T.Zaeschke
- Fixed Issue #111 and #112 (partially reverting issue #75):
Support for attaching detached objects.
2017-Sep-28
===========
@@ -20,9 +20,6 @@
*/
package org.zoodb.api.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import javax.jdo.ObjectState;
@@ -352,7 +349,7 @@ public final void jdoZooInit(ObjectState state, PCContext bundle, long oid) {
}
case PERSISTENT_DIRTY: {
//This should only be called from on-demand evolution in GenericObjects
if (!(this instanceof GenericObject)) {
if (!(this instanceof GenericObject) && !jdoZooIsDetached()) {
throw new UnsupportedOperationException("" + state);
}
setPersDirty();
@@ -187,13 +187,36 @@ public final void traverse() {
// DBLogger.debugPrintln(1, "Finished OGT: " + nObjects + " (seen="
// + seenObjects.size() + " ) / " + (t2-t1)/1000.0
// + " MP=" + mpCount);
traverseCache();
traverseWorkList();
//We have to clear the seenObjects here, see also issue #58.
seenObjects.clear();
try {
traverseCache();
traverseWorkList();
} finally {
workList.clear();
toBecomePersistent.clear();
//We have to clear the seenObjects here, see also issue #58.
seenObjects.clear();
}
traversalRequired = false;
}
/**
* This can be used to enforce traversal of an object even if it is PERSITENT_CLEAN.
* This can be necessary for objects that transition from DETACHED_CLEAN
* to PERSITENT_CLEAN and whose children need to make this transition transitively.
* @param pc Object to check.
*/
public final void traverse(ZooPC pc) {
try {
traverseObject(pc);
traverseWorkList();
} finally {
workList.clear();
toBecomePersistent.clear();
//We have to clear the seenObjects here, see also issue #58.
seenObjects.clear();
}
}
private int traverseCache() {
isTraversingCache = true;
int nObjects = 0;
@@ -85,4 +85,14 @@ public void ensureValidity(long oid) {
}
}
public static boolean isValid(long oid) {
if (oid > 0) {
return true;
}
if (oid == Session.OID_NOT_ASSIGNED) {
return false;
}
throw DBLogger.newUser("Invalid OID: " + oid);
}
}
@@ -65,6 +65,7 @@
public static final Logger LOGGER = LoggerFactory.getLogger(Session.class);
/** See also OidBuffer for OID handling methods. */
public static final long OID_NOT_ASSIGNED = -1;
public static final long TIMESTAMP_NOT_ASSIGNED = -1;
@@ -104,7 +105,7 @@ public Session(SessionParentCallback parentSession, String dbPath, SessionConfig
this.parentSession = parentSession;
this.config = config;
this.primary = ZooFactory.get().createNode(dbPath, this);
this.cache = new ClientSessionCache(this);
this.cache = new ClientSessionCache(this, primary);
this.schemaManager = new SchemaManager(cache, config.getAutoCreateSchema());
this.nodes.add(primary);
this.cache.addNode(primary);
@@ -442,12 +443,6 @@ public void makeTransient(ZooPC pc) {
}
}
public static void assertOid(long oid) {
if (oid == OID_NOT_ASSIGNED) {
throw DBLogger.newUser("Invalid OID: " + oid);
}
}
/**
* INTERNAL !!!!
* @param cls Class
@@ -31,8 +31,7 @@
public abstract void rollback();
public abstract void markPersistent(ZooPC pc, long oid, Node node,
ZooClassDef clsDef);
public abstract void markPersistent(ZooPC pc, Node node, ZooClassDef clsDef);
public abstract ZooPC findCoByOID(long oid);
@@ -34,6 +34,7 @@
import org.zoodb.internal.GenericObject;
import org.zoodb.internal.Node;
import org.zoodb.internal.ObjectGraphTraverser;
import org.zoodb.internal.OidBuffer;
import org.zoodb.internal.Session;
import org.zoodb.internal.ZooClassDef;
import org.zoodb.internal.client.AbstractCache;
@@ -77,12 +78,14 @@
private final Session session;
private final ObjectGraphTraverser ogt;
private final OidBuffer oidBuffer;
private ZooClassDef metaSchema;
public ClientSessionCache(Session session) {
public ClientSessionCache(Session session, Node primary) {
this.session = session;
this.ogt = new ObjectGraphTraverser(this);
this.oidBuffer = primary.getOidBuffer();
switch (session.getConfig().getCacheMode()) {
case WEAK: objs = new PrimLongMapZWeak<ZooPC>(); break;
@@ -186,7 +189,7 @@ public void rollback() {
@Override
public final void markPersistent(ZooPC pc, long oid, Node node, ZooClassDef clsDef) {
public final void markPersistent(ZooPC pc, Node node, ZooClassDef clsDef) {
if (pc.jdoZooIsDeleted()) {
throw new UnsupportedOperationException("Make it persistent again");
//TODO implement
@@ -196,7 +199,29 @@ public final void markPersistent(ZooPC pc, long oid, Node node, ZooClassDef clsD
return;
}
addToCache(pc, clsDef, oid, ObjectState.PERSISTENT_NEW);
long oid = pc.jdoZooGetOid();
if (OidBuffer.isValid(oid)) {
//We have an OID. This can happen during re-attach or when the OID is explicitly set
if (pc.jdoZooIsDetached()) {
//Reattaching object? We need to ensure transitivity. The special case here is
//Objects that change to CLEAN, because the won't be checked by the OGT.
if (objs.containsKey(oid)) {
//We have to check and fail _before_ we change the status of the object.
throw DBLogger.newUser("The session already contains an object with the same "
+ "OID (OID conflict): " + oid);
}
if (!pc.jdoZooIsDirty()) {
ogt.traverse(pc);
}
addToCache(pc, clsDef, oid, pc.jdoZooIsDirty() ?
ObjectState.PERSISTENT_DIRTY : ObjectState.PERSISTENT_CLEAN);
} else {
addToCache(pc, clsDef, oid, ObjectState.PERSISTENT_NEW);
}
} else {
oid = oidBuffer.allocateOid();
addToCache(pc, clsDef, oid, ObjectState.PERSISTENT_NEW);
}
}
@@ -224,7 +249,11 @@ public final void addToCache(ZooPC obj, ZooClassDef classDef, long oid,
obj.jdoZooInit(state, classDef.getProvidedContext(), oid);
//TODO call newInstance elsewhere
//obj.jdoReplaceStateManager(co);
objs.put(obj.jdoZooGetOid(), obj);
Object result = objs.putIfAbsent(obj.jdoZooGetOid(), obj);
if (result != null) {
throw DBLogger.newUser("The session already contains an object with the same "
+ "OID (OID conflict): " + oid);
}
}
@@ -200,10 +200,8 @@ public final void makePersistent(ZooPC obj) {
} else {
cs = ((GenericObject)obj).jdoZooGetClassDef();
}
//allocate OID
long oid = getOidBuffer().allocateOid();
//add to cache
commonCache.markPersistent(obj, oid, this, cs);
commonCache.markPersistent(obj, this, cs);
}
@Override
@@ -22,6 +22,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -32,6 +33,16 @@
public abstract T put(long keyBits, T obj);
/**
*
* @param keyBits The key
* @param obj The value
* @return The prvious value or 'null' the the key did not exist.
* @see {@link Map#putIfAbsent(Object, Object)}
*/
@SuppressWarnings("javadoc")
public abstract T putIfAbsent(long keyBits, T obj);
public abstract T remove(long keyBits);
public abstract int size();
@@ -159,6 +159,26 @@ public T put(long keyBits, T obj) {
return null;
}
@Override
public T putIfAbsent(long keyBits, T obj) {
if (obj == null) {
throw new IllegalArgumentException("Value must not be null.");
}
int pos = calcHash(keyBits);
Entry<T> e = entries[pos];
while (e != null && e.key != keyBits) {
e = e.next;
}
if (e != null) {
return e.getValue();
}
modCount++;
checkRehash(size + 1);
putEntryNoCheck(new Entry<T>(keyBits, obj));
size++;
return null;
}
@Override
public T remove(long keyBits) {
int pos = calcHash(keyBits);
@@ -165,6 +165,26 @@ public T put(long keyBits, T obj) {
return null;
}
@Override
public T putIfAbsent(long keyBits, T obj) {
if (obj == null) {
throw new IllegalArgumentException("Value must not be null.");
}
int pos = calcHash(keyBits);
Entry<T> e = entries[pos];
while (e != null && e.key != keyBits) {
e = e.next;
}
if (e != null) {
return e.getValue();
}
modCount++;
checkRehash(size + 1);
putEntryNoCheck(new Entry<T>(keyBits, obj));
size++;
return null;
}
@Override
public T remove(long keyBits) {
int pos = calcHash(keyBits);
@@ -165,6 +165,26 @@ public T put(long keyBits, T obj) {
return null;
}
@Override
public T putIfAbsent(long keyBits, T obj) {
if (obj == null) {
throw new IllegalArgumentException("Value must not be null.");
}
int pos = calcHash(keyBits);
Entry<T> e = entries[pos];
while (e != null && e.key != keyBits) {
e = e.next;
}
if (e != null) {
return e.getValue();
}
modCount++;
checkRehash(size + 1);
putEntryNoCheck(new Entry<T>(keyBits, obj));
size++;
return null;
}
@Override
public T remove(long keyBits) {
int pos = calcHash(keyBits);
Oops, something went wrong.

0 comments on commit 73d0d5f

Please sign in to comment.