Skip to content

Commit

Permalink
Fixed issue #111 and #112, partially reverting #75. This handles reat…
Browse files Browse the repository at this point in the history
…taching detached objects.
  • Loading branch information
tzaeschke committed Sep 17, 2018
1 parent c9d64ba commit 73d0d5f
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 33 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG
@@ -1,5 +1,10 @@
CHANGELOG CHANGELOG


2018-Sep-17
===========
- T.Zaeschke
- Fixed Issue #111 and #112 (partially reverting issue #75):
Support for attaching detached objects.


2017-Sep-28 2017-Sep-28
=========== ===========
Expand Down
5 changes: 1 addition & 4 deletions src/org/zoodb/api/impl/ZooPC.java
Expand Up @@ -20,9 +20,6 @@
*/ */
package org.zoodb.api.impl; package org.zoodb.api.impl;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable; import java.io.Serializable;


import javax.jdo.ObjectState; import javax.jdo.ObjectState;
Expand Down Expand Up @@ -352,7 +349,7 @@ public final void jdoZooInit(ObjectState state, PCContext bundle, long oid) {
} }
case PERSISTENT_DIRTY: { case PERSISTENT_DIRTY: {
//This should only be called from on-demand evolution in GenericObjects //This should only be called from on-demand evolution in GenericObjects
if (!(this instanceof GenericObject)) { if (!(this instanceof GenericObject) && !jdoZooIsDetached()) {
throw new UnsupportedOperationException("" + state); throw new UnsupportedOperationException("" + state);
} }
setPersDirty(); setPersDirty();
Expand Down
31 changes: 27 additions & 4 deletions src/org/zoodb/internal/ObjectGraphTraverser.java
Expand Up @@ -187,13 +187,36 @@ public final void traverse() {
// DBLogger.debugPrintln(1, "Finished OGT: " + nObjects + " (seen=" // DBLogger.debugPrintln(1, "Finished OGT: " + nObjects + " (seen="
// + seenObjects.size() + " ) / " + (t2-t1)/1000.0 // + seenObjects.size() + " ) / " + (t2-t1)/1000.0
// + " MP=" + mpCount); // + " MP=" + mpCount);
traverseCache(); try {
traverseWorkList(); traverseCache();
//We have to clear the seenObjects here, see also issue #58. traverseWorkList();
seenObjects.clear(); } finally {
workList.clear();
toBecomePersistent.clear();
//We have to clear the seenObjects here, see also issue #58.
seenObjects.clear();
}
traversalRequired = false; 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() { private int traverseCache() {
isTraversingCache = true; isTraversingCache = true;
int nObjects = 0; int nObjects = 0;
Expand Down
10 changes: 10 additions & 0 deletions src/org/zoodb/internal/OidBuffer.java
Expand Up @@ -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);
}

} }
9 changes: 2 additions & 7 deletions src/org/zoodb/internal/Session.java
Expand Up @@ -65,6 +65,7 @@ public class Session implements IteratorRegistry {


public static final Logger LOGGER = LoggerFactory.getLogger(Session.class); 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 OID_NOT_ASSIGNED = -1;
public static final long TIMESTAMP_NOT_ASSIGNED = -1; public static final long TIMESTAMP_NOT_ASSIGNED = -1;


Expand Down Expand Up @@ -104,7 +105,7 @@ public Session(SessionParentCallback parentSession, String dbPath, SessionConfig
this.parentSession = parentSession; this.parentSession = parentSession;
this.config = config; this.config = config;
this.primary = ZooFactory.get().createNode(dbPath, this); 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.schemaManager = new SchemaManager(cache, config.getAutoCreateSchema());
this.nodes.add(primary); this.nodes.add(primary);
this.cache.addNode(primary); this.cache.addNode(primary);
Expand Down Expand Up @@ -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 !!!! * INTERNAL !!!!
* @param cls Class * @param cls Class
Expand Down
3 changes: 1 addition & 2 deletions src/org/zoodb/internal/client/AbstractCache.java
Expand Up @@ -31,8 +31,7 @@ public interface AbstractCache {


public abstract void rollback(); public abstract void rollback();


public abstract void markPersistent(ZooPC pc, long oid, Node node, public abstract void markPersistent(ZooPC pc, Node node, ZooClassDef clsDef);
ZooClassDef clsDef);


public abstract ZooPC findCoByOID(long oid); public abstract ZooPC findCoByOID(long oid);


Expand Down
37 changes: 33 additions & 4 deletions src/org/zoodb/internal/client/session/ClientSessionCache.java
Expand Up @@ -34,6 +34,7 @@
import org.zoodb.internal.GenericObject; import org.zoodb.internal.GenericObject;
import org.zoodb.internal.Node; import org.zoodb.internal.Node;
import org.zoodb.internal.ObjectGraphTraverser; import org.zoodb.internal.ObjectGraphTraverser;
import org.zoodb.internal.OidBuffer;
import org.zoodb.internal.Session; import org.zoodb.internal.Session;
import org.zoodb.internal.ZooClassDef; import org.zoodb.internal.ZooClassDef;
import org.zoodb.internal.client.AbstractCache; import org.zoodb.internal.client.AbstractCache;
Expand Down Expand Up @@ -77,12 +78,14 @@ public class ClientSessionCache implements AbstractCache {


private final Session session; private final Session session;
private final ObjectGraphTraverser ogt; private final ObjectGraphTraverser ogt;
private final OidBuffer oidBuffer;


private ZooClassDef metaSchema; private ZooClassDef metaSchema;


public ClientSessionCache(Session session) { public ClientSessionCache(Session session, Node primary) {
this.session = session; this.session = session;
this.ogt = new ObjectGraphTraverser(this); this.ogt = new ObjectGraphTraverser(this);
this.oidBuffer = primary.getOidBuffer();


switch (session.getConfig().getCacheMode()) { switch (session.getConfig().getCacheMode()) {
case WEAK: objs = new PrimLongMapZWeak<ZooPC>(); break; case WEAK: objs = new PrimLongMapZWeak<ZooPC>(); break;
Expand Down Expand Up @@ -186,7 +189,7 @@ public void rollback() {




@Override @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()) { if (pc.jdoZooIsDeleted()) {
throw new UnsupportedOperationException("Make it persistent again"); throw new UnsupportedOperationException("Make it persistent again");
//TODO implement //TODO implement
Expand All @@ -196,7 +199,29 @@ public final void markPersistent(ZooPC pc, long oid, Node node, ZooClassDef clsD
return; 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);
}
} }




Expand Down Expand Up @@ -224,7 +249,11 @@ public final void addToCache(ZooPC obj, ZooClassDef classDef, long oid,
obj.jdoZooInit(state, classDef.getProvidedContext(), oid); obj.jdoZooInit(state, classDef.getProvidedContext(), oid);
//TODO call newInstance elsewhere //TODO call newInstance elsewhere
//obj.jdoReplaceStateManager(co); //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);
}
} }




Expand Down
4 changes: 1 addition & 3 deletions src/org/zoodb/internal/model1p/Node1P.java
Expand Up @@ -200,10 +200,8 @@ public final void makePersistent(ZooPC obj) {
} else { } else {
cs = ((GenericObject)obj).jdoZooGetClassDef(); cs = ((GenericObject)obj).jdoZooGetClassDef();
} }
//allocate OID
long oid = getOidBuffer().allocateOid();
//add to cache //add to cache
commonCache.markPersistent(obj, oid, this, cs); commonCache.markPersistent(obj, this, cs);
} }


@Override @Override
Expand Down
11 changes: 11 additions & 0 deletions src/org/zoodb/internal/util/PrimLongMap.java
Expand Up @@ -22,6 +22,7 @@


import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Set; import java.util.Set;




Expand All @@ -32,6 +33,16 @@ public interface PrimLongMap<T> {


public abstract T put(long keyBits, T obj); 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 T remove(long keyBits);


public abstract int size(); public abstract int size();
Expand Down
20 changes: 20 additions & 0 deletions src/org/zoodb/internal/util/PrimLongMapZ.java
Expand Up @@ -159,6 +159,26 @@ public T put(long keyBits, T obj) {
return null; 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 @Override
public T remove(long keyBits) { public T remove(long keyBits) {
int pos = calcHash(keyBits); int pos = calcHash(keyBits);
Expand Down
20 changes: 20 additions & 0 deletions src/org/zoodb/internal/util/PrimLongMapZSoft.java
Expand Up @@ -165,6 +165,26 @@ public T put(long keyBits, T obj) {
return null; 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 @Override
public T remove(long keyBits) { public T remove(long keyBits) {
int pos = calcHash(keyBits); int pos = calcHash(keyBits);
Expand Down
20 changes: 20 additions & 0 deletions src/org/zoodb/internal/util/PrimLongMapZWeak.java
Expand Up @@ -165,6 +165,26 @@ public T put(long keyBits, T obj) {
return null; 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 @Override
public T remove(long keyBits) { public T remove(long keyBits) {
int pos = calcHash(keyBits); int pos = calcHash(keyBits);
Expand Down

0 comments on commit 73d0d5f

Please sign in to comment.