Index: test.sh
===================================================================
RCS file: /scm/castor/castor/test.sh,v
retrieving revision 1.2
diff -u -r1.2 test.sh
--- test.sh 9 Mar 2005 20:45:43 -0000 1.2
+++ test.sh 26 Aug 2005 13:45:28 -0000
@@ -18,7 +18,7 @@
CLASSPATH=`echo lib/*.jar | tr ' ' ':'`:$CLASSPATH
CLASSPATH=`echo lib/tests/*.jar | tr ' ' ':'`:$CLASSPATH
-$JAVA -cp $CLASSPATH Main -execute $1 $2 $3 $4 $5 $6
+$JAVA -Djava.net.preferIPv4Stack=true -cp $CLASSPATH Main -execute $1 $2 $3 $4 $5 $6
Index: src/bugs/AllTests.java
===================================================================
RCS file: /scm/castor/castor/src/bugs/AllTests.java,v
retrieving revision 1.2
diff -u -r1.2 AllTests.java
--- src/bugs/AllTests.java 7 Apr 2005 20:52:19 -0000 1.2
+++ src/bugs/AllTests.java 26 Aug 2005 13:45:28 -0000
@@ -7,7 +7,7 @@
suite.addTestSuite(jdo.template.TestTemplate.class);
// suite.addTestSuite(jdo.bug1900.TestFieldWithSpace.class);
-
+ suite.addTestSuite(jdo.bug1196.TestLongTransaction.class);
return suite;
}
Index: src/main/org/castor/persist/resolver/PersistanceCapableRelationResolver.java
===================================================================
RCS file: /scm/castor/castor/src/main/org/castor/persist/resolver/PersistanceCapableRelationResolver.java,v
retrieving revision 1.1
diff -u -r1.1 PersistanceCapableRelationResolver.java
--- src/main/org/castor/persist/resolver/PersistanceCapableRelationResolver.java 22 Aug 2005 16:29:05 -0000 1.1
+++ src/main/org/castor/persist/resolver/PersistanceCapableRelationResolver.java 26 Aug 2005 13:45:33 -0000
@@ -258,33 +258,60 @@
/* (non-Javadoc)
* @see org.castor.persist.resolver.ResolverStrategy#update(org.castor.persist.TransactionContext, org.exolab.castor.persist.OID, java.lang.Object, org.exolab.castor.mapping.AccessMode, java.lang.Object)
*/
- public void update(TransactionContext tx, OID oid, Object object, AccessMode suggestedAccessMode, Object field)
+ public void update(TransactionContext tx, OID oid, Object object, AccessMode suggestedAccessMode, Object oldValue)
throws PersistenceException, ObjectModifiedException
{
- ClassMolder fieldClassMolder = fieldMolder.getFieldClassMolder();
LockEngine fieldEngine = fieldMolder.getFieldLockEngine();
- Object o = fieldMolder.getValue( object, tx.getClassLoader() );
- if ( fieldMolder.isDependent() ) {
+ Object fieldNewValue = fieldMolder.getValue( object, tx.getClassLoader() );
+
+ //get fieldClassMolder based on fieldNewValue with this we get correct
+ //ClassMolder based on value, for class hierarchy.
+ //e.g.: the field Class is Product but value is Computer
+ ClassMolder fieldClassMolder = null;
+ if (fieldNewValue == null) {
+ fieldClassMolder = fieldMolder.getFieldClassMolder();
+ } else {
+ fieldClassMolder = fieldEngine.getClassMolder(fieldNewValue.getClass());
+ // TODO [CW]: fieldEngine.getClassMolder returns null when fieldNewValue is dependent
+ // but note the referer points to a dependent of another class
+ // e.g.: B depends A and C references B.
+ // the trouble occurs on C field, follow the temporary workarround
+ if (fieldClassMolder == null) {
+ fieldClassMolder = fieldMolder.getFieldClassMolder();
+ }
+ }
+
+ if ( fieldMolder.isDependent() ) {
// depedent class won't have persistenceInfo in LockEngine
// must look at fieldMolder for it
- if ( o != null && !tx.isRecorded(o) )
- tx.markUpdate( fieldEngine, fieldClassMolder, o, oid );
+ if ( fieldNewValue != null && !tx.isRecorded(fieldNewValue) )
+ tx.markUpdate( fieldEngine, fieldClassMolder, fieldNewValue, oid );
// load the cached dependent object from the data store.
// The loaded will be compared with the new one
- if ( field != null ) {
+ if ( oldValue != null ) {
ProposedObject proposedValue = new ProposedObject ();
- tx.load(fieldEngine, fieldClassMolder, field, proposedValue, suggestedAccessMode);
+ tx.load(fieldEngine, fieldClassMolder, oldValue, proposedValue, suggestedAccessMode);
}
} else if ( tx.isAutoStore() ) {
- if ( o != null && !tx.isRecorded(o) )
- tx.markUpdate( fieldEngine, fieldClassMolder, o, null );
+ if ( fieldNewValue != null && !tx.isRecorded(fieldNewValue) )
+ tx.markUpdate( fieldEngine, fieldClassMolder, fieldNewValue, null );
- if ( field != null ) {
+ if ( oldValue != null ) {
ProposedObject proposedValue = new ProposedObject ();
- tx.load(fieldEngine, fieldClassMolder, field, proposedValue, suggestedAccessMode);
+ tx.load(fieldEngine, fieldClassMolder, oldValue, proposedValue, suggestedAccessMode);
}
+ } else if (fieldNewValue != null) {
+ Object oIdentity = fieldClassMolder.getIdentity(tx,fieldNewValue);
+ if ( oIdentity != null
+ && ((oldValue == null) || !oldValue.equals(oIdentity))
+ && !tx.isRecorded(fieldNewValue)
+ && !fieldClassMolder.hasBackReferenceTo(object.getClass())) {
+ ProposedObject proposedValue = new ProposedObject();
+ tx.load(fieldEngine, fieldClassMolder, oIdentity, proposedValue,suggestedAccessMode);
+ fieldMolder.setValue(object,proposedValue.getObject(),tx.getClassLoader());
+ }
}
}
Index: src/main/org/exolab/castor/persist/ClassMolder.java
===================================================================
RCS file: /scm/castor/castor/src/main/org/exolab/castor/persist/ClassMolder.java,v
retrieving revision 1.28
diff -u -r1.28 ClassMolder.java
--- src/main/org/exolab/castor/persist/ClassMolder.java 23 Aug 2005 02:01:55 -0000 1.28
+++ src/main/org/exolab/castor/persist/ClassMolder.java 26 Aug 2005 13:45:37 -0000
@@ -495,6 +495,28 @@
}
/**
+ * Checks if data object represented by this ClassMolder, has a reference to a referer
+ * @param referer is a Class that references the data object of this ClassMolder
+ */
+ public boolean hasBackReferenceTo(Class refererClass) {
+ ClassMolder fieldClassMolder;
+ int fieldType;
+
+ for ( int i=0; i<_fhs.length; i++ ) {
+ fieldType = _fhs[i].getFieldType();
+ fieldClassMolder = _fhs[i].getFieldClassMolder();
+ switch (fieldType) {
+ case FieldMolder.PERSISTANCECAPABLE:
+ if (fieldClassMolder.isAssignableFrom(refererClass))
+ return true;
+
+ }
+ }
+ return false;
+ }
+
+
+ /**
* Load an object with specified identity from the persistent storage.
*
* @param tx the TransactionContext in action
@@ -877,15 +899,15 @@
ClassMolder fieldClassMolder;
LockEngine fieldEngine;
- Object[] fields;
+ Object[] objectOldValues;
Object stamp;
int fieldType;
- Object o;
+ Object fieldNewValue;
AccessMode accessMode = getAccessMode( suggestedAccessMode );
resetResolvers();
- fields = (Object[]) locker.getObject( tx );
+ objectOldValues = (Object[]) locker.getObject( tx );
if ((!isDependent()) && (!_timeStampable)) {
throw new IllegalArgumentException("A master object that involves in a long transaction must be a TimeStampable!");
@@ -911,27 +933,27 @@
if ( _timeStampable && objectTimestamp != lockTimestamp )
throw new ObjectModifiedException("Timestamp mismatched!");
- if ( !_timeStampable && isDependent() && fields == null ) {
+ if ( !_timeStampable && isDependent() && objectOldValues == null ) {
// allow a dependent object not implements timeStampable
- fields = new Object[_fhs.length];
+ objectOldValues = new Object[_fhs.length];
Connection conn = tx.getConnection(oid.getLockEngine());
ProposedObject proposedObject = new ProposedObject();
proposedObject.setProposedClass(object.getClass());
proposedObject.setObject(object);
- proposedObject.setFields(fields);
+ proposedObject.setFields(objectOldValues);
stamp = _persistence.load(conn, proposedObject, oid.getIdentity(), accessMode);
- fields = proposedObject.getFields();
+ objectOldValues = proposedObject.getFields();
oid.setDbLock( accessMode == AccessMode.DbLocked );
- locker.setObject( tx, fields );
+ locker.setObject( tx, objectOldValues );
}
// load the original field into the transaction. so, store will
// have something to compare later.
try {
for ( int i=0; i <_fhs.length; i++ ) {
- _resolvers[i].update(tx, oid, object, accessMode, fields[i]);
+ _resolvers[i].update(tx, oid, object, accessMode, objectOldValues[i]);
}
} catch ( ObjectNotFoundException e ) {
e.printStackTrace();
@@ -954,10 +976,21 @@
case FieldMolder.PERSISTANCECAPABLE:
// create dependent object if exists
- fieldClassMolder = _fhs[i].getFieldClassMolder();
+ //fieldClassMolder = _fhs[i].getFieldClassMolder();
fieldEngine = _fhs[i].getFieldLockEngine();
- o = _fhs[i].getValue( object, tx.getClassLoader() );
- if ( o != null ) {
+ fieldNewValue = _fhs[i].getValue( object, tx.getClassLoader() );
+ if ( fieldNewValue != null ) {
+ //get fieldClassMolder based on fieldNewValue with this we get correct
+ //ClassMolder based on value, for class hierarchy.
+ //e.g.: the field Class is Product but value is Computer
+ fieldClassMolder = fieldEngine.getClassMolder(fieldNewValue.getClass());
+ // TODO [CW]: fieldEngine.getClassMolder returns null when fieldNewValue is dependent
+ // but note the referer points to a dependent of another class
+ // e.g.: B depends A and C references B.
+ // the trouble occurs on C field, follow the temporary workarround
+ if (fieldClassMolder == null) {
+ fieldClassMolder = _fhs[i].getFieldClassMolder();
+ }
if ( _fhs[i].isDependent() ) {
// creation of dependent object should be delayed to the
// preStore state.
@@ -969,8 +1002,8 @@
// the only disadvantage for that appoarch is that an
// OQL Query will not able to include the newly generated
// dependent object.
- if ( !tx.isRecorded( o ) ) {
- tx.markCreate( fieldEngine, fieldClassMolder, o, oid );
+ if ( !tx.isRecorded( fieldNewValue ) ) {
+ tx.markCreate( fieldEngine, fieldClassMolder, fieldNewValue, oid );
if ( !_fhs[i].isStored() && fieldClassMolder._isKeyGenUsed ) {
updateCache = true;
}
@@ -980,12 +1013,12 @@
// if ( !tx.isDepended( oid, o ) )
// throw new PersistenceException("Dependent object may not change its master. Object: "+o+" new master: "+oid);
} else if ( tx.isAutoStore() ) {
- if ( !tx.isRecorded( o ) ) {
+ if ( !tx.isRecorded( fieldNewValue ) ) {
// related object should be created right the way, if autoStore
// is enabled, to obtain a database lock on the row. If both side
// uses keygenerator, the current object will be updated in the
// store state.
- boolean creating = tx.markUpdate( fieldEngine, fieldClassMolder, o, null );
+ boolean creating = tx.markUpdate( fieldEngine, fieldClassMolder, fieldNewValue, null );
// if _fhs[i].isStore is true for this field,
// and if key generator is used
// and if the related object is replaced this object by null
@@ -1003,6 +1036,23 @@
updateCache = true;
}
}
+ } else {
+ // TODO [CW]: the test case ctf.jdo.tc2x.TestDependentKeyGenUpdate.test(TestDependentKeyGenUpdate.java:379)
+ // fails with NullPointerException if fieldClassMolder is null, because what a
+ // fieldClassMolder is null? this is the work around, i will only log
+ try {
+ Object oIdentity = fieldClassMolder.getIdentity(tx,fieldNewValue);
+ if ( oIdentity != null
+ && ((objectOldValues[i] == null) || !objectOldValues[i].equals(oIdentity))
+ && !tx.isRecorded(fieldNewValue)
+ && !fieldClassMolder.hasBackReferenceTo(object.getClass())) {
+ ProposedObject proposedValue = new ProposedObject();
+ tx.load(fieldEngine, fieldClassMolder, oIdentity, proposedValue,suggestedAccessMode);
+ _fhs[i].setValue(object,proposedValue.getObject(),tx.getClassLoader());
+ }
+ } catch (NullPointerException nullException) {
+ _log.warn("field "+_fhs[i]+" of object "+object+" dont have fieldClassMolder!",nullException);
+ }
}
}
break;
@@ -1011,9 +1061,9 @@
// create dependent objects if exists
fieldClassMolder = _fhs[i].getFieldClassMolder();
fieldEngine = _fhs[i].getFieldLockEngine();
- o = _fhs[i].getValue( object, tx.getClassLoader() );
- if ( o != null ) {
- Iterator itor = ClassMolderHelper.getIterator( o );
+ fieldNewValue = _fhs[i].getValue( object, tx.getClassLoader() );
+ if ( fieldNewValue != null ) {
+ Iterator itor = ClassMolderHelper.getIterator( fieldNewValue );
while (itor.hasNext()) {
Object oo = itor.next();
if ( _fhs[i].isDependent() ) {
@@ -1042,9 +1092,9 @@
// create relation if the relation table
fieldClassMolder = _fhs[i].getFieldClassMolder();
fieldEngine = _fhs[i].getFieldLockEngine();
- o = _fhs[i].getValue( object, tx.getClassLoader() );
- if ( o != null ) {
- Iterator itor = ClassMolderHelper.getIterator( o );
+ fieldNewValue = _fhs[i].getValue( object, tx.getClassLoader() );
+ if ( fieldNewValue != null ) {
+ Iterator itor = ClassMolderHelper.getIterator( fieldNewValue );
// many-to-many relation is never dependent relation
while (itor.hasNext()) {
Object oo = itor.next();
Index: src/tests/jdo/sapdb.xml
===================================================================
RCS file: /scm/castor/castor/src/tests/jdo/sapdb.xml,v
retrieving revision 1.6
diff -u -r1.6 sapdb.xml
--- src/tests/jdo/sapdb.xml 7 Aug 2005 09:55:01 -0000 1.6
+++ src/tests/jdo/sapdb.xml 26 Aug 2005 13:45:38 -0000
@@ -3,9 +3,9 @@
"http://castor.exolab.org/jdo-conf.dtd">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: src/bugs/jdo/bug1196/mapping.xml
===================================================================
RCS file: src/bugs/jdo/bug1196/mapping.xml
diff -N src/bugs/jdo/bug1196/mapping.xml
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/bugs/jdo/bug1196/mapping.xml 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+ Country
+
+
+
+
+
+
+
+
+
+
+
+
+ State Country
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Product
+
+
+
+
+
+
+
+
+
+
+
+
+ Computer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Order
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OrderItem
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Car
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Driver
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: src/bugs/jdo/bug1196/mysql.sql
===================================================================
RCS file: src/bugs/jdo/bug1196/mysql.sql
diff -N src/bugs/jdo/bug1196/mysql.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/bugs/jdo/bug1196/mysql.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,84 @@
+DROP TABLE if exists STATECOUNTRY;
+
+DROP TABLE if exists COUNTRY;
+
+CREATE TABLE COUNTRY (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ PRIMARY KEY (OID)
+);
+
+CREATE TABLE STATECOUNTRY (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_COUNTRY VARCHAR(8) NOT NULL,
+ PRIMARY KEY (OID)
+);
+
+ALTER TABLE STATECOUNTRY ADD FOREIGN KEY (MY_COUNTRY) REFERENCES COUNTRY(OID);
+
+DROP TABLE if exists COMPUTER;
+
+DROP TABLE if exists ORDERITEM;
+
+DROP TABLE if exists PRODUCT;
+
+DROP TABLE if exists TBORDER;
+
+CREATE TABLE TBORDER (
+ OID VARCHAR(8) NOT NULL,
+ NUMBER_OF INTEGER NOT NULL,
+ PRIMARY KEY (OID)
+);
+
+CREATE TABLE PRODUCT (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ PRIMARY KEY (OID)
+);
+
+CREATE TABLE ORDERITEM (
+ OID VARCHAR(8) NOT NULL,
+ QUANTITY INTEGER NOT NULL,
+ MY_PRODUCT VARCHAR(8) NOT NULL,
+ MY_ORDER VARCHAR(8) NOT NULL,
+ PRIMARY KEY (OID)
+);
+
+ALTER TABLE ORDERITEM ADD FOREIGN KEY (MY_PRODUCT) REFERENCES PRODUCT(OID);
+
+ALTER TABLE ORDERITEM ADD FOREIGN KEY (MY_ORDER) REFERENCES TBORDER(OID);
+
+CREATE TABLE COMPUTER (
+ OID VARCHAR(8) NOT NULL,
+ SERIAL_NUMBER VARCHAR(20) NOT NULL,
+ MY_ORDER_ITEM VARCHAR(8),
+ PRIMARY KEY (OID)
+);
+
+ALTER TABLE COMPUTER ADD FOREIGN KEY (OID) REFERENCES PRODUCT(OID);
+
+ALTER TABLE COMPUTER ADD FOREIGN KEY (MY_ORDER_ITEM) REFERENCES ORDERITEM(OID);
+
+DROP TABLE if exists CAR;
+
+DROP TABLE if exists DRIVER;
+
+CREATE TABLE CAR (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_DRIVER VARCHAR(8),
+ PRIMARY KEY (OID)
+);
+
+CREATE TABLE DRIVER (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_CURRENT_CAR VARCHAR(8),
+ PRIMARY KEY (OID)
+);
+
+ALTER TABLE CAR ADD FOREIGN KEY (MY_DRIVER) REFERENCES DRIVER(OID);
+
+ALTER TABLE DRIVER ADD FOREIGN KEY (MY_CURRENT_CAR) REFERENCES CAR(OID);
+
Index: src/bugs/jdo/bug1196/sapdb.sql
===================================================================
RCS file: src/bugs/jdo/bug1196/sapdb.sql
diff -N src/bugs/jdo/bug1196/sapdb.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/bugs/jdo/bug1196/sapdb.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,85 @@
+DROP TABLE COUNTRY
+//
+CREATE TABLE COUNTRY (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ PRIMARY KEY (OID)
+)
+//
+DROP TABLE STATECOUNTRY
+//
+CREATE TABLE STATECOUNTRY (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_COUNTRY VARCHAR(8) NOT NULL,
+ PRIMARY KEY (OID)
+)
+//
+ALTER TABLE STATECOUNTRY ADD FOREIGN KEY (MY_COUNTRY) REFERENCES COUNTRY(OID)
+//
+DROP TABLE PRODUCT
+//
+CREATE TABLE PRODUCT (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ PRIMARY KEY (OID)
+)
+//
+DROP TABLE COMPUTER
+//
+CREATE TABLE COMPUTER (
+ OID VARCHAR(8) NOT NULL,
+ SERIAL_NUMBER VARCHAR(20) NOT NULL,
+ MY_ORDER_ITEM VARCHAR(8),
+ PRIMARY KEY (OID)
+)
+//
+ALTER TABLE COMPUTER ADD FOREIGN KEY (OID) REFERENCES PRODUCT(OID)
+//
+ALTER TABLE COMPUTER ADD FOREIGN KEY (MY_ORDER_ITEM) REFERENCES ORDERITEM(OID)
+//
+DROP TABLE TBORDER
+//
+CREATE TABLE TBORDER (
+ OID VARCHAR(8) NOT NULL,
+ NUMBER_OF INTEGER NOT NULL,
+ PRIMARY KEY (OID)
+)
+//
+DROP TABLE ORDERITEM
+//
+CREATE TABLE ORDERITEM (
+ OID VARCHAR(8) NOT NULL,
+ QUANTITY INTEGER NOT NULL,
+ MY_PRODUCT VARCHAR(8) NOT NULL,
+ MY_ORDER VARCHAR(8) NOT NULL,
+ PRIMARY KEY (OID)
+)
+//
+ALTER TABLE ORDERITEM ADD FOREIGN KEY (MY_PRODUCT) REFERENCES PRODUCT(OID)
+//
+ALTER TABLE ORDERITEM ADD FOREIGN KEY (MY_ORDER) REFERENCES TBORDER(OID)
+//
+
+DROP TABLE CAR
+//
+CREATE TABLE CAR (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_DRIVER VARCHAR(8),
+ PRIMARY KEY (OID)
+)
+//
+DROP TABLE DRIVER
+//
+CREATE TABLE DRIVER (
+ OID VARCHAR(8) NOT NULL,
+ NAME VARCHAR(60) NOT NULL,
+ MY_CURRENT_CAR VARCHAR(8),
+ PRIMARY KEY (OID)
+)
+//
+ALTER TABLE CAR ADD FOREIGN KEY (MY_DRIVER) REFERENCES DRIVER(OID)
+//
+ALTER TABLE DRIVER ADD FOREIGN KEY (MY_CURRENT_CAR) REFERENCES CAR(OID)
+//