Index: src/java/org/apache/jdo/tck/lifecycle/CheckStatesOfReturnedObjects.java =================================================================== --- src/java/org/apache/jdo/tck/lifecycle/CheckStatesOfReturnedObjects.java (revision 0) +++ src/java/org/apache/jdo/tck/lifecycle/CheckStatesOfReturnedObjects.java (revision 0) @@ -0,0 +1,679 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.tck.lifecycle; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Iterator; + +import javax.jdo.Extent; +import javax.jdo.JDOFatalException; +import javax.jdo.Transaction; + +import org.apache.jdo.tck.JDO_Test; +import org.apache.jdo.tck.pc.lifecycle.StateTransitionObj; +import org.apache.jdo.tck.util.BatchTestRunner; + +/** + *Title: Test State Transitions + *
+ *Keywords: lifecycle + *
+ *Assertion IDs: A5.9-1 through A5.9-190 + *Assertion Description: + All possible state transistions are being tested in this test. + */ + +public class CheckStatesOfReturnedObjects extends JDO_Test { + + /** */ + private static final String ASSERTION_FAILED = + "Assertions A5.9-1 through A5.5.8 (serialization, detachment, attachment) failed: "; + + /** + * The main is called when the class + * is directly executed from the command line. + * @param args The arguments passed to the program. + */ + public static void main(String[] args) { + BatchTestRunner.run(CheckStatesOfReturnedObjects.class); + } + + private Transaction transaction; + private int scenario; + private int operation; + private int current_state; + private int expected_state; + private int new_state; + +/** + * Operations that cause state changes + */ + private static final int MAKEPERSISTENT = 0; + private static final int DETACHCOPYOUTSIDETX = 1; + private static final int DETACHCOPYINSIDETX = 2; + private static final int SERIALIZEOUTSIDETX = 3; + private static final int SERIALIZEINSIDETX = 4; + + private static final int NUM_OPERATIONS = 5; + + private static final String[] operations = { + "makePersistent", + "detachCopy outside tx", + "detachCopy with active tx", + "serialize outside tx", + "serialize with active tx" + }; + + /** + * Illegal state transitions + */ + private static final int UNCHANGED = -1; + private static final int ERROR = -2; + private static final int IMPOSSIBLE = -3; + private static final int NOT_APPLICABLE = -4; + + /** + * State transitions + */ + public static final int[][] statesOfReturnedObjects = { // [operation] [current state] = new state + // TRANSIENT, PERSISTENT_NEW, PERSISTENT_CLEAN, + // PERSISTENT_DIRTY, HOLLOW, TRANSIENT_CLEAN, + // TRANSIENT_DIRTY, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + // PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL_DIRTY, DETACHED_CLEAN, + // DETACHED_DIRTY + + // makePersistent + { PERSISTENT_NEW, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, PERSISTENT_NEW, + PERSISTENT_NEW, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, /*Bug: PERSISTENT_CLEAN*/PERSISTENT_DIRTY, + PERSISTENT_DIRTY}, + + // detachCopy outside tx + { ERROR, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, DETACHED_CLEAN, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + DETACHED_CLEAN, ERROR, UNCHANGED, + UNCHANGED}, + + // detachCopy with active tx + { /*Bug: DETACHED_CLEAN*/UNCHANGED,DETACHED_CLEAN, DETACHED_CLEAN, + DETACHED_CLEAN, DETACHED_CLEAN, /*Bug: DETACHED_CLEAN*/TRANSIENT, + /*Bug: DETACHED_CLEAN*/TRANSIENT,ERROR, ERROR, + DETACHED_CLEAN, ERROR, UNCHANGED, + UNCHANGED}, + + // serialize outside tx + { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, DETACHED_CLEAN, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + DETACHED_CLEAN, TRANSIENT, UNCHANGED, + UNCHANGED}, + + // serialize with active tx + { UNCHANGED, /*Bug: DETACHED_CLEAN*/TRANSIENT, DETACHED_CLEAN, + DETACHED_CLEAN, DETACHED_CLEAN, TRANSIENT, + TRANSIENT, TRANSIENT, TRANSIENT, + DETACHED_CLEAN, TRANSIENT, UNCHANGED, + UNCHANGED}, + }; + + private static final int DATASTORE_TX = 0; + private static final int OPTIMISTIC_TX = 1; + private static final int NO_TX = 2; + + private static final String[] scenario_string = { + "datastore transaction", "optimistic transaction", "no transaction" + }; + + private static final boolean[][] applies_to_scenario = { + // Datastore Optimistic No tx + { true, true, false }, // makePersistent + { false, false, true }, // detachCopy outside tx + { true, true, false }, // detachCopy with active tx + { false, false, true }, // serialize outside tx + { true, true, false } // serialize with active tx + }; + + /** + * @see JDO_Test#localSetUp() + */ + protected void localSetUp() { + pm = getPM(); + addTearDownClass(StateTransitionObj.class); + generatePersistentInstances(); + } + + public void test() { + scenario = DATASTORE_TX; + checkTransitions(); + + if( isOptimisticSupported() ){ + scenario = OPTIMISTIC_TX; + checkTransitions(); + } + + scenario = NO_TX; + checkTransitions(); + } + + /** */ + private void generatePersistentInstances() + { + if( doPersistentInstancesExist() ) return; + int i; + Transaction t = pm.currentTransaction(); + t.begin(); + for( i = 0; i < 50; ++i ){ + StateTransitionObj sto = new StateTransitionObj(i); + sto.writeField(i); + pm.makePersistent(sto); + } + t.commit(); + if( !doPersistentInstancesExist() ) + if (debug) + logger.debug("CheckStatesOfReturnedObjects unable to create instances of StateTransitionsObj"); + } + + /** */ + private boolean doPersistentInstancesExist() + { + boolean ret; + Transaction t = pm.currentTransaction(); + t.begin(); + Extent e = pm.getExtent(StateTransitionObj.class, false); + Iterator iter = e.iterator(); + ret = iter.hasNext(); + t.rollback(); + return ret; + } + + /** */ + public void prepareTransactionAndJDOSettings(Transaction transaction) { + if( scenario != NO_TX ) { + transaction.setNontransactionalRead(false); + transaction.setNontransactionalWrite(false); + pm.setDetachAllOnCommit(false); + + transaction.setOptimistic(scenario == OPTIMISTIC_TX); + transaction.begin(); + if( !transaction.isActive() ) + if (debug) + logger.debug("CheckStatesOfReturnedObjects: Transaction should be active, but it is not"); + } else { + if( operation == DETACHCOPYOUTSIDETX || + operation == SERIALIZEOUTSIDETX ) { + transaction.setNontransactionalRead(true); + } + if( current_state == PERSISTENT_NONTRANSACTIONAL_DIRTY) { + transaction.setNontransactionalRead(true); + } + } + } + + /** */ + void checkTransitions() + { + for( operation = 0; operation < NUM_OPERATIONS; ++operation ){ + // rule out situations that do not apply + if( !applies_to_scenario[operation][scenario] ) continue; + if( (operation == DETACHCOPYOUTSIDETX || + operation == SERIALIZEOUTSIDETX) && + !isNontransactionalReadSupported() ) continue; + + for( current_state = 0; current_state < NUM_STATES; ++current_state){ + if( scenario == OPTIMISTIC_TX && current_state == PERSISTENT_CLEAN ) continue; + if( (current_state == TRANSIENT_CLEAN || current_state == TRANSIENT_DIRTY) && + !isTransientTransactionalSupported() ) + continue; // this state is not supported by implementation + if( current_state == PERSISTENT_NONTRANSACTIONAL && + !(isNontransactionalReadSupported() || isNontransactionalWriteSupported()) ) + continue; // this state is not supported by implementation + if( current_state == PERSISTENT_NONTRANSACTIONAL_DIRTY && + !isNontransactionalWriteSupported() ) + continue; + + expected_state = statesOfReturnedObjects[operation][current_state]; + if( expected_state == IMPOSSIBLE ) continue; + if( expected_state == NOT_APPLICABLE ) continue; + if( expected_state == UNCHANGED ) expected_state = current_state; + try { + transaction = pm.currentTransaction(); + if( transaction.isActive()){ + if (debug) + logger.debug("Transaction is active (but should not be), rolling back"); + transaction.rollback(); + } + + prepareTransactionAndJDOSettings(transaction); + + printSituation(); + + StateTransitionObj obj = getInstanceInState(current_state); + if( obj == null ){ // could not get object in state + if( transaction.isActive() ) transaction.rollback(); + continue; + } + + // Apply operation, catching possible exception + Exception e = null; + Object returnedObject = null; + try { + returnedObject = applyOperation(operation, obj); + } catch( Exception excep ){ + if( excep instanceof javax.jdo.JDOUserException ){ + e = excep; + } else { + fail(ASSERTION_FAILED, + "CheckStatesOfReturnedObjects: Unexpected exception:" + excep); + continue; + } + } + + if( expected_state == ERROR ){ + if( e == null ){ + fail(ASSERTION_FAILED, + "CheckStatesOfReturnedObjects: JDOUserException should have been thrown"); + } else { + int stateOfObj = currentState(obj); + if( stateOfObj != current_state ){ + fail(ASSERTION_FAILED, + "CheckStatesOfReturnedObjects: " + + " JDOUserException properly thrown, but instance should remain in current state," + + " instance changed state to " + states[stateOfObj]); + } + } + } else { + // Get new state, verify correct transition and exceptions occurred + new_state = currentState(returnedObject); + if( expected_state >= 0 && new_state != expected_state && + !((new_state == HOLLOW && expected_state == PERSISTENT_NONTRANSACTIONAL) || + (new_state == PERSISTENT_NONTRANSACTIONAL && expected_state == HOLLOW)) ) { + // status interrogation gives same values for PERSISTENT_NONTRANSACTIONAL and HOLLOW + fail(ASSERTION_FAILED, + "CheckStatesOfReturnedObjects: Invalid state " + states[new_state] + + ", returned by operation " + operations[operation] + + ", returned state should be " + states[expected_state]); + } + } + if( transaction.isActive() ) transaction.rollback(); + } + catch(Exception unexpected_exception) { + if (transaction.isActive()) + transaction.rollback(); + fail(ASSERTION_FAILED, + "Unexpected exception caught in CheckStatesOfReturnedObjects " + unexpected_exception); + } + } + } + } + + /** */ + void printSituation() + { + if (debug) { + logger.debug(" (" + scenario_string[scenario] + + ", initial state=" + states[current_state] + + ", operation=" + operations[operation] + ")"); + } + } + + /** */ + Object applyOperation(int operation, StateTransitionObj stobj) + { + Object result = null; + StateTransitionObj obj = (StateTransitionObj) stobj; + switch( operation ){ + case MAKEPERSISTENT: + result = pm.makePersistent(obj); + break; + + case DETACHCOPYOUTSIDETX: + case DETACHCOPYINSIDETX: + result = pm.detachCopy(obj); + break; + + case SERIALIZEOUTSIDETX: + case SERIALIZEINSIDETX: + ObjectOutputStream oos = null; + ObjectInputStream ois = null; + try { + ByteArrayOutputStream byteArrayOutputStream = + new ByteArrayOutputStream(); + oos = new ObjectOutputStream(byteArrayOutputStream); + oos.writeObject(obj); + ois = new ObjectInputStream(new ByteArrayInputStream( + byteArrayOutputStream.toByteArray())); + result = ois.readObject(); + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } catch (ClassNotFoundException e) { + throw new JDOFatalException(e.getMessage(), e); + } finally { + try { + if (oos != null) { + oos.close(); + } + if (ois != null) { + ois.close(); + } + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } + } + break; + + default: + fail(ASSERTION_FAILED, + "CheckStatesOfReturnedObjects internal error, illegal operation " + operation); + } + return result; + } + + /** + * Get an instance in the specified state. + */ + public StateTransitionObj getInstanceInState(int state) + { + switch(state) { + case TRANSIENT: + return getTransientInstance(); + case PERSISTENT_NEW: + return getPersistentNewInstance(); + case PERSISTENT_CLEAN: + return getPersistentCleanInstance(); + case PERSISTENT_DIRTY: + return getPersistentDirtyInstance(); + case HOLLOW: + return getHollowInstance(); + case TRANSIENT_CLEAN: + return getTransientCleanInstance(); + case TRANSIENT_DIRTY: + return getTransientDirtyInstance(); + case PERSISTENT_NEW_DELETED: + return getPersistentNewDeletedInstance(); + case PERSISTENT_DELETED: + return getPersistentDeletedInstance(); + case PERSISTENT_NONTRANSACTIONAL: + return getPersistentNontransactionalInstance(); + case PERSISTENT_NONTRANSACTIONAL_DIRTY: + return getPersistentNontransactionalDirtyInstance(); + case DETACHED_CLEAN: + return getDetachedCleanInstance(); + case DETACHED_DIRTY: + return getDetachedDirtyInstance(); + default: + return null; + } + } + + /** */ + public StateTransitionObj getTransientInstance() + { + StateTransitionObj obj = new StateTransitionObj(23); + int curr = currentState(obj); + if( curr != TRANSIENT ) { + if (debug) { + logger.debug("CheckStatesOfReturnedObjects: Unable to create transient instance, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentNewInstance() + { + StateTransitionObj obj = getTransientInstance(); + if( obj == null ) return null; + pm.makePersistent(obj); // should transition to persistent-new + int curr = currentState(obj); + if( curr != PERSISTENT_NEW ) { + if (debug) { + logger.debug("CheckStatesOfReturnedObjects: Unable to create persistent-new instance" + + " from transient instance via makePersistent(), state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentCleanInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + StateTransitionObj sto = (StateTransitionObj) obj; + sto.readField(); + int curr = currentState(sto); + if( curr != PERSISTENT_CLEAN ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-clean instance" + + " from a hollow instance by reading a field, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentDirtyInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + StateTransitionObj pcobj = (StateTransitionObj) obj; + pcobj.writeField(23); + int curr = currentState(obj); + if( curr != PERSISTENT_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-dirty instance" + + " from a hollow instance by writing a field, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getHollowInstance() + { + Extent extent = pm.getExtent(StateTransitionObj.class, false); + Iterator iter = extent.iterator(); + if( !iter.hasNext() ){ + if (debug) + logger.debug("Extent for StateTransitionObj should not be empty"); + return null; + } + StateTransitionObj obj = (StateTransitionObj) iter.next(); + + transaction.setRetainValues(false); + if ( !transaction.isActive() ) + transaction.begin(); + if( !transaction.isActive() ) + if (debug) + logger.debug("getHollowInstance: Transaction should be active, but it is not"); + + transaction.commit(); // This should put the instance in the HOLLOW state + + prepareTransactionAndJDOSettings(transaction); + + int curr = currentState(obj); + if( curr != HOLLOW && curr != PERSISTENT_NONTRANSACTIONAL ){ + if (debug) { + logger.debug("StateTransition: Attempt to get hollow instance via accessing extent failed, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getTransientCleanInstance() + { + StateTransitionObj obj = getTransientInstance(); + if( obj == null ) return null; + pm.makeTransactional(obj); + int curr = currentState(obj); + if( curr != TRANSIENT_CLEAN ) { + if (debug) { + logger.debug("StateTransition: Unable to create transient-clean instance" + + " from a transient instance via makeTransactional(), state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getTransientDirtyInstance() + { + StateTransitionObj obj = getTransientCleanInstance(); + if( obj == null ) return null; + StateTransitionObj pcobj = (StateTransitionObj) obj; + pcobj.writeField(23); + int curr = currentState(obj); + if( curr != TRANSIENT_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create transient-dirty instance" + + " from a transient-clean instance via modifying a field, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentNewDeletedInstance() + { + StateTransitionObj obj = getPersistentNewInstance(); + if( obj == null ) return null; + pm.deletePersistent(obj); // should transition to persistent-new-deleted + int curr = currentState(obj); + if( curr != PERSISTENT_NEW_DELETED) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-new-deleted instance" + + " from a persistent-new instance via deletePersistent, state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentDeletedInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + pm.deletePersistent(obj); + int curr = currentState(obj); + if( curr != PERSISTENT_DELETED ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-deleted instance" + + " from a persistent instance via deletePersistent(), state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentNontransactionalInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + pm.makeNontransactional(obj); + int curr = currentState(obj); + if( curr != PERSISTENT_NONTRANSACTIONAL && curr != HOLLOW ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-nontransactional instance" + + " from a persistent-clean instance via makeNontransactional(), state is " + + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getPersistentNontransactionalDirtyInstance() + { + StateTransitionObj obj = getPersistentNontransactionalInstance(); + if( obj == null ) return null; + obj.writeField(10000); + int curr = currentState(obj); + if( curr != PERSISTENT_NONTRANSACTIONAL_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-nontransactional-dirty instance" + + " from a persistent-clean instance via makeNontransactional()/JDOHelper.makeDirty," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getDetachedCleanInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + obj = (StateTransitionObj) pm.detachCopy(obj); + int curr = currentState(obj); + if( curr != DETACHED_CLEAN ) { + if (debug) { + logger.debug("StateTransition: Unable to create detached-clean instance" + + " from a persistent-clean instance via detachCopy," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getDetachedDirtyInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + obj = (StateTransitionObj) pm.detachCopy(obj); + obj.writeField(1000); + int curr = currentState(obj); + if( curr != DETACHED_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create detached-dirty instance" + + " from a persistent-clean instance via detachCopy/persistent field modification," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + +} Index: src/java/org/apache/jdo/tck/lifecycle/StateTransitions.java =================================================================== --- src/java/org/apache/jdo/tck/lifecycle/StateTransitions.java (revision 371522) +++ src/java/org/apache/jdo/tck/lifecycle/StateTransitions.java (working copy) @@ -16,9 +16,13 @@ package org.apache.jdo.tck.lifecycle; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.util.Iterator; import javax.jdo.Extent; +import javax.jdo.JDOFatalException; import javax.jdo.Transaction; import org.apache.jdo.tck.JDO_Test; @@ -79,7 +83,14 @@ private static final int WRITEINSIDETX = 16; private static final int RETRIEVEOUTSIDETX = 17; private static final int RETRIEVEINSIDETX = 18; - + private static final int DETACHALLONCOMMIT = 19; + private static final int DETACHCOPYOUTSIDETX = 20; + private static final int DETACHCOPYINSIDETX = 21; + private static final int SERIALIZEOUTSIDETX = 22; + private static final int SERIALIZEINSIDETX = 23; + + private static final int NUM_OPERATIONS = 24; + private static final String[] operations = { "makePersistent", "deletePersistent", @@ -99,14 +110,14 @@ "write field outside tx", "write field with active tx", "retrieve outside tx", - "retrieve with active tx" + "retrieve with active tx", + "commit, detachAllOnCommit=true", + "detachCopy outside tx", + "detachCopy with active tx", + "serialize outside tx", + "serialize with active tx" }; - private static final int NUM_OPERATIONS = 19; - private static final boolean[] closes_transaction = - { false, false, false, false, false, true, true, true, true, false, - false, false, false, false, false, false, false, false, false }; - /** * Illegal state transitions */ @@ -119,82 +130,197 @@ * State transitions */ public static final int[][] transitions = { // [operation] [current state] = new state + // TRANSIENT, PERSISTENT_NEW, PERSISTENT_CLEAN, + // PERSISTENT_DIRTY, HOLLOW, TRANSIENT_CLEAN, + // TRANSIENT_DIRTY, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + // PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL_DIRTY, DETACHED_CLEAN, + // DETACHED_DIRTY + // makePersistent - { PERSISTENT_NEW, UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_NEW, - PERSISTENT_NEW, UNCHANGED, UNCHANGED, UNCHANGED}, + { PERSISTENT_NEW, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, PERSISTENT_NEW, + PERSISTENT_NEW, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, DETACHED_CLEAN, + DETACHED_DIRTY}, // deletePersistent - { ERROR, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, PERSISTENT_DELETED, - PERSISTENT_DELETED, ERROR, ERROR, UNCHANGED, UNCHANGED, PERSISTENT_DELETED}, + { ERROR, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + PERSISTENT_DELETED, PERSISTENT_DELETED, ERROR, + ERROR, UNCHANGED, UNCHANGED, + PERSISTENT_DELETED, PERSISTENT_DELETED, ERROR, + ERROR}, // makeTransactional - { TRANSIENT_CLEAN, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN, - UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN}, + { TRANSIENT_CLEAN, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_CLEAN, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + PERSISTENT_CLEAN, PERSISTENT_DIRTY, /*Bug: ERROR*/TRANSIENT_CLEAN, + /*Bug: ERROR*/TRANSIENT_CLEAN}, // makeNontransactional - { ERROR, ERROR, PERSISTENT_NONTRANSACTIONAL, ERROR, UNCHANGED, - TRANSIENT, ERROR, ERROR, ERROR, UNCHANGED}, + { ERROR, ERROR, PERSISTENT_NONTRANSACTIONAL, + ERROR, UNCHANGED, TRANSIENT, + ERROR, ERROR, ERROR, + UNCHANGED, UNCHANGED, ERROR, + ERROR}, // makeTransient - { UNCHANGED, ERROR, TRANSIENT, ERROR, TRANSIENT, - UNCHANGED, UNCHANGED, ERROR, ERROR, TRANSIENT}, + { UNCHANGED, ERROR, TRANSIENT, + ERROR, TRANSIENT, UNCHANGED, + UNCHANGED, ERROR, ERROR, + TRANSIENT, /*Bug: ERROR*/TRANSIENT, /*Bug: ERROR*/UNCHANGED, + /*Bug: ERROR*/UNCHANGED}, // commit, retainValues = false - { UNCHANGED, HOLLOW, HOLLOW, HOLLOW, UNCHANGED, UNCHANGED, - TRANSIENT_CLEAN, TRANSIENT, TRANSIENT, UNCHANGED}, + { UNCHANGED, HOLLOW, HOLLOW, + HOLLOW, UNCHANGED, UNCHANGED, + TRANSIENT_CLEAN, TRANSIENT, TRANSIENT, + UNCHANGED, HOLLOW, UNCHANGED, + UNCHANGED}, // commit, retainValues = true - { UNCHANGED, PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL, - PERSISTENT_NONTRANSACTIONAL, UNCHANGED, UNCHANGED, TRANSIENT_CLEAN, - TRANSIENT, TRANSIENT, UNCHANGED}, + { UNCHANGED, PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL, + PERSISTENT_NONTRANSACTIONAL, UNCHANGED, UNCHANGED, + TRANSIENT_CLEAN, TRANSIENT, TRANSIENT, + UNCHANGED, PERSISTENT_NONTRANSACTIONAL, UNCHANGED, + UNCHANGED}, + // TRANSIENT, PERSISTENT_NEW, PERSISTENT_CLEAN, + // PERSISTENT_DIRTY, HOLLOW, TRANSIENT_CLEAN, + // TRANSIENT_DIRTY, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + // PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL_DIRTY, DETACHED_CLEAN, + // DETACHED_DIRTY + // rollback, restoreValues = false - { UNCHANGED, TRANSIENT, HOLLOW, HOLLOW, UNCHANGED, UNCHANGED, - TRANSIENT_CLEAN, TRANSIENT, HOLLOW, UNCHANGED}, + { UNCHANGED, TRANSIENT, HOLLOW, + HOLLOW, UNCHANGED, UNCHANGED, + TRANSIENT_CLEAN, TRANSIENT, HOLLOW, + UNCHANGED, HOLLOW, UNCHANGED, + UNCHANGED}, // rollback, restoreValues = true - { UNCHANGED, TRANSIENT, PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL, - UNCHANGED, UNCHANGED, TRANSIENT_CLEAN, TRANSIENT, PERSISTENT_NONTRANSACTIONAL, UNCHANGED}, + { UNCHANGED, TRANSIENT, PERSISTENT_NONTRANSACTIONAL, + PERSISTENT_NONTRANSACTIONAL, UNCHANGED, UNCHANGED, + TRANSIENT_CLEAN, TRANSIENT, PERSISTENT_NONTRANSACTIONAL, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, // refresh with active datastore transaction - { UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN, UNCHANGED, - UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED}, + { UNCHANGED, UNCHANGED, UNCHANGED, + PERSISTENT_CLEAN, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_NONTRANSACTIONAL, UNCHANGED, + UNCHANGED}, // refresh with active optimistic transaction - { UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_NONTRANSACTIONAL, UNCHANGED, - UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED}, + { UNCHANGED, UNCHANGED, UNCHANGED, + PERSISTENT_NONTRANSACTIONAL, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_NONTRANSACTIONAL, UNCHANGED, + UNCHANGED}, // evict - { NOT_APPLICABLE, UNCHANGED, HOLLOW, UNCHANGED, UNCHANGED, - UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, HOLLOW}, + { NOT_APPLICABLE, UNCHANGED, HOLLOW, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + HOLLOW, HOLLOW, UNCHANGED, + UNCHANGED}, // read field outside transaction - { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, - UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, UNCHANGED}, + { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, // read field with active optimistic transaction - { UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_NONTRANSACTIONAL, - UNCHANGED, UNCHANGED, ERROR, ERROR, UNCHANGED}, + { UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_NONTRANSACTIONAL, UNCHANGED, + UNCHANGED, ERROR, ERROR, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, + // TRANSIENT, PERSISTENT_NEW, PERSISTENT_CLEAN, + // PERSISTENT_DIRTY, HOLLOW, TRANSIENT_CLEAN, + // TRANSIENT_DIRTY, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + // PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL_DIRTY, DETACHED_CLEAN, + // DETACHED_DIRTY + // read field with active datastore transaction - { UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN, - UNCHANGED, UNCHANGED, ERROR, ERROR, PERSISTENT_CLEAN}, + { UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_CLEAN, UNCHANGED, + UNCHANGED, ERROR, ERROR, + PERSISTENT_CLEAN, UNCHANGED, UNCHANGED, + UNCHANGED}, // write field outside transaction - { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, - UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, UNCHANGED}, + { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + UNCHANGED, UNCHANGED, DETACHED_DIRTY, + UNCHANGED}, // write field with active transaction - { UNCHANGED, UNCHANGED, PERSISTENT_DIRTY, UNCHANGED, PERSISTENT_DIRTY, - TRANSIENT_DIRTY, UNCHANGED, ERROR, ERROR, PERSISTENT_DIRTY}, - + { UNCHANGED, UNCHANGED, PERSISTENT_DIRTY, + UNCHANGED, PERSISTENT_DIRTY, TRANSIENT_DIRTY, + UNCHANGED, ERROR, ERROR, + PERSISTENT_DIRTY, UNCHANGED, DETACHED_DIRTY, + UNCHANGED}, + // retrieve outside transaction - { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, - UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, UNCHANGED}, - + { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, PERSISTENT_NONTRANSACTIONAL, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, + // retrieve with active transaction - { UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN, - UNCHANGED, UNCHANGED, UNCHANGED, UNCHANGED, PERSISTENT_CLEAN} + { UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, PERSISTENT_CLEAN, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + PERSISTENT_CLEAN, UNCHANGED, UNCHANGED, + UNCHANGED}, + + // commit, detachAllOnCommit = true + { UNCHANGED, DETACHED_CLEAN, DETACHED_CLEAN, + DETACHED_CLEAN, /*Bug: DETACHED_CLEAN*/UNCHANGED, UNCHANGED, + TRANSIENT_CLEAN, /*Bug: TRANSIENT*/NOT_APPLICABLE, /*Bug: TRANSIENT*/NOT_APPLICABLE, + /*Bug: DETACHED_CLEAN*/HOLLOW, DETACHED_CLEAN, UNCHANGED, + UNCHANGED}, + + // TRANSIENT, PERSISTENT_NEW, PERSISTENT_CLEAN, + // PERSISTENT_DIRTY, HOLLOW, TRANSIENT_CLEAN, + // TRANSIENT_DIRTY, PERSISTENT_NEW_DELETED, PERSISTENT_DELETED, + // PERSISTENT_NONTRANSACTIONAL, PERSISTENT_NONTRANSACTIONAL_DIRTY, DETACHED_CLEAN, + // DETACHED_DIRTY + + // detachCopy outside tx + { ERROR, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, UNCHANGED, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + UNCHANGED, ERROR, UNCHANGED, + UNCHANGED}, + + // detachCopy with active tx + { /*Bug: PERSISTENT_NEW*/UNCHANGED,UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, /*Bug: PERSISTENT_NEW*/UNCHANGED, + /*Bug: PERSISTENT_NEW*/UNCHANGED,ERROR, ERROR, + UNCHANGED, ERROR, UNCHANGED, + UNCHANGED}, + + // serialize outside tx + { UNCHANGED, IMPOSSIBLE, IMPOSSIBLE, + IMPOSSIBLE, UNCHANGED, IMPOSSIBLE, + IMPOSSIBLE, IMPOSSIBLE, IMPOSSIBLE, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, + + // serialize with active tx + { UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED, UNCHANGED, UNCHANGED, + UNCHANGED}, }; private static final int DATASTORE_TX = 0; @@ -225,7 +351,12 @@ { false, false, true }, // write field or makeDirty outside of a transaction { true, true, false }, // write field or makeDirty with active transaction { false, true, true }, // retrieve outside of a transaction or with active optimistic transaction - { true, false, false } // retrieve with active datastore transaction + { true, false, false }, // retrieve with active datastore transaction + { true, true, false }, // commit, DetachAllOnCommit = true + { false, false, true }, // detachCopy outside tx + { true, true, false }, // detachCopy with active tx + { false, false, true }, // serialize outside tx + { true, true, false } // serialize with active tx }; /** @@ -282,12 +413,54 @@ } /** */ + public void prepareTransactionAndJDOSettings(Transaction transaction) { + if( scenario != NO_TX ) { + transaction.setNontransactionalRead(false); + transaction.setNontransactionalWrite(false); + + if( operation == COMMITNORETAINVALUES ) + transaction.setRetainValues(false); + if( operation == COMMITRETAINVALUES ) + transaction.setRetainValues(true); + if( operation == ROLLBACKNORESTOREVALUES ) + transaction.setRestoreValues(false); + if( operation == ROLLBACKRESTOREVALUES ) + transaction.setRestoreValues(true); + if( operation == DETACHALLONCOMMIT ) + pm.setDetachAllOnCommit(true); + else + pm.setDetachAllOnCommit(false); + + transaction.setOptimistic(scenario == OPTIMISTIC_TX); + transaction.begin(); + if( !transaction.isActive() ) + if (debug) + logger.debug("StateTransitions: Transaction should be active, but it is not"); + } else { + if( operation == READOUTSIDETX || + operation == RETRIEVEOUTSIDETX || + operation == DETACHCOPYOUTSIDETX || + operation == SERIALIZEOUTSIDETX ) { + transaction.setNontransactionalRead(true); + } + if( operation == WRITEOUTSIDETX || + current_state == PERSISTENT_NONTRANSACTIONAL_DIRTY) { + transaction.setNontransactionalWrite(true); + } + } + } + + /** */ void checkTransitions() { for( operation = 0; operation < NUM_OPERATIONS; ++operation ){ // rule out situations that do not apply - if( ! applies_to_scenario[operation][scenario] ) continue; - if( operation == READOUTSIDETX && !isNontransactionalReadSupported() ) continue; + if( !applies_to_scenario[operation][scenario] ) continue; + if( (operation == READOUTSIDETX || + operation == RETRIEVEOUTSIDETX || + operation == DETACHCOPYOUTSIDETX || + operation == SERIALIZEOUTSIDETX) && + !isNontransactionalReadSupported() ) continue; if( operation == WRITEOUTSIDETX && !isNontransactionalWriteSupported() ) continue; if( operation == COMMITRETAINVALUES && !isRetainValuesSupported() ) continue; if( operation == MAKENONTRANSACTIONAL && @@ -302,6 +475,9 @@ if( current_state == PERSISTENT_NONTRANSACTIONAL && !(isNontransactionalReadSupported() || isNontransactionalWriteSupported()) ) continue; // this state is not supported by implementation + if( current_state == PERSISTENT_NONTRANSACTIONAL_DIRTY && + !isNontransactionalWriteSupported() ) + continue; expected_state = transitions[operation][current_state]; if( expected_state == IMPOSSIBLE ) continue; @@ -315,23 +491,10 @@ transaction.rollback(); } - if( scenario != NO_TX ){ - if( operation == COMMITNORETAINVALUES ) - transaction.setRetainValues(false); - if( operation == COMMITRETAINVALUES ) - transaction.setRetainValues(true); - if( operation == ROLLBACKNORESTOREVALUES ) - transaction.setRestoreValues(false); - if( operation == ROLLBACKRESTOREVALUES ) - transaction.setRestoreValues(true); + prepareTransactionAndJDOSettings(transaction); - transaction.setOptimistic(scenario == OPTIMISTIC_TX); - transaction.begin(); - if( !transaction.isActive() ) - if (debug) - logger.debug("StateTransitions: Transaction should be active, but it is not"); - } - + printSituation(); + StateTransitionObj obj = getInstanceInState(current_state); if( obj == null ){ // could not get object in state if( transaction.isActive() ) transaction.rollback(); @@ -346,7 +509,6 @@ if( excep instanceof javax.jdo.JDOUserException ){ e = excep; } else { - printSituation(); fail(ASSERTION_FAILED, "StateTransitions: Unexpected exception:" + excep); continue; @@ -357,12 +519,10 @@ new_state = currentState(obj); if( expected_state == ERROR ){ if( e == null ){ - printSituation(); fail(ASSERTION_FAILED, "StateTransitions: JDOUserException should have been thrown"); } else { if( new_state != current_state ){ - printSituation(); fail(ASSERTION_FAILED, "StateTransitions: " + " JDOUserException properly thrown, but instance should remain in current state," + @@ -374,7 +534,6 @@ !((new_state == HOLLOW && expected_state == PERSISTENT_NONTRANSACTIONAL) || (new_state == PERSISTENT_NONTRANSACTIONAL && expected_state == HOLLOW)) ) { // status interrogation gives same values for PERSISTENT_NONTRANSACTIONAL and HOLLOW - printSituation(); fail(ASSERTION_FAILED, "StateTransitions: Invalid state transition to " + states[new_state] + ", new state should be " + @@ -383,7 +542,6 @@ if( transaction.isActive() ) transaction.rollback(); } catch(Exception unexpected_exception) { - printSituation(); if (transaction.isActive()) transaction.rollback(); fail(ASSERTION_FAILED, @@ -470,17 +628,17 @@ } case READOUTSIDETX: { - int val = obj.readField(); + obj.readField(); break; } case READOPTIMISTIC: { - int val = obj.readField(); + obj.readField(); break; } case READDATASTORE: { - int val = obj.readField(); + obj.readField(); break; } case WRITEOUTSIDETX: @@ -501,8 +659,61 @@ case RETRIEVEINSIDETX: { pm.retrieve(obj); - break; + break; } + case DETACHALLONCOMMIT: + { + pm.currentTransaction().commit(); + break; + } + case DETACHCOPYOUTSIDETX: + { + pm.detachCopy(obj); + break; + } + case DETACHCOPYINSIDETX: + { + pm.detachCopy(obj); + break; + } + case SERIALIZEOUTSIDETX: + { + ObjectOutputStream oos = null; + try { + oos = new ObjectOutputStream(new ByteArrayOutputStream()); + oos.writeObject(obj); + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } + } + } + break; + } + case SERIALIZEINSIDETX: + { + ObjectOutputStream oos = null; + try { + oos = new ObjectOutputStream(new ByteArrayOutputStream()); + oos.writeObject(obj); + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + throw new JDOFatalException(e.getMessage(), e); + } + } + } + break; + } default: { fail(ASSERTION_FAILED, @@ -514,7 +725,7 @@ /** * Get an instance in the specified state. */ - private StateTransitionObj getInstanceInState(int state) + public StateTransitionObj getInstanceInState(int state) { switch(state) { case TRANSIENT: @@ -537,15 +748,19 @@ return getPersistentDeletedInstance(); case PERSISTENT_NONTRANSACTIONAL: return getPersistentNontransactionalInstance(); + case PERSISTENT_NONTRANSACTIONAL_DIRTY: + return getPersistentNontransactionalDirtyInstance(); + case DETACHED_CLEAN: + return getDetachedCleanInstance(); + case DETACHED_DIRTY: + return getDetachedDirtyInstance(); default: - { return null; } - } } /** */ - private StateTransitionObj getTransientInstance() + public StateTransitionObj getTransientInstance() { StateTransitionObj obj = new StateTransitionObj(23); int curr = currentState(obj); @@ -554,14 +769,13 @@ logger.debug("StateTransitions: Unable to create transient instance, state is " + states[curr]); } - printSituation(); return null; } return obj; } /** */ - private StateTransitionObj getPersistentNewInstance() + public StateTransitionObj getPersistentNewInstance() { StateTransitionObj obj = getTransientInstance(); if( obj == null ) return null; @@ -573,7 +787,6 @@ " from transient instance via makePersistent(), state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -585,7 +798,7 @@ StateTransitionObj obj = getHollowInstance(); if( obj == null ) return null; StateTransitionObj sto = (StateTransitionObj) obj; - int val = sto.readField(); + sto.readField(); int curr = currentState(sto); if( curr != PERSISTENT_CLEAN ) { if (debug) { @@ -593,7 +806,6 @@ " from a hollow instance by reading a field, state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -613,7 +825,6 @@ " from a hollow instance by writing a field, state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -637,33 +848,17 @@ if( !transaction.isActive() ) if (debug) logger.debug("getHollowInstance: Transaction should be active, but it is not"); - + transaction.commit(); // This should put the instance in the HOLLOW state - if( scenario != NO_TX ){ - if( operation == COMMITNORETAINVALUES ) - transaction.setRetainValues(false); - if( operation == COMMITRETAINVALUES ) - transaction.setRetainValues(true); - if( operation == ROLLBACKNORESTOREVALUES ) - transaction.setRestoreValues(false); - if( operation == ROLLBACKRESTOREVALUES ) - transaction.setRestoreValues(true); + prepareTransactionAndJDOSettings(transaction); - transaction.setOptimistic(scenario == OPTIMISTIC_TX); - transaction.begin(); - if( !transaction.isActive() ) - if (debug) - logger.debug("getHollowInstance: Transaction should be active, but it is not"); - } - int curr = currentState(obj); if( curr != HOLLOW && curr != PERSISTENT_NONTRANSACTIONAL ){ if (debug) { logger.debug("StateTransition: Attempt to get hollow instance via accessing extent failed, state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -682,7 +877,6 @@ " from a transient instance via makeTransactional(), state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -702,7 +896,6 @@ " from a transient-clean instance via modifying a field, state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -721,7 +914,6 @@ " from a persistent-new instance via deletePersistent, state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -740,7 +932,6 @@ " from a persistent instance via deletePersistent(), state is " + states[curr]); } - printSituation(); return null; } return obj; @@ -759,10 +950,64 @@ " from a persistent-clean instance via makeNontransactional(), state is " + states[curr]); } - printSituation(); return null; } - return null; + return obj; } + /** */ + public StateTransitionObj getPersistentNontransactionalDirtyInstance() + { + StateTransitionObj obj = getPersistentNontransactionalInstance(); + if( obj == null ) return null; + obj.writeField(10000); + int curr = currentState(obj); + if( curr != PERSISTENT_NONTRANSACTIONAL_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create persistent-nontransactional-dirty instance" + + " from a persistent-clean instance via makeNontransactional()/JDOHelper.makeDirty," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getDetachedCleanInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + obj = (StateTransitionObj) pm.detachCopy(obj); + int curr = currentState(obj); + if( curr != DETACHED_CLEAN ) { + if (debug) { + logger.debug("StateTransition: Unable to create detached-clean instance" + + " from a persistent-clean instance via detachCopy," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + + /** */ + public StateTransitionObj getDetachedDirtyInstance() + { + StateTransitionObj obj = getHollowInstance(); + if( obj == null ) return null; + obj = (StateTransitionObj) pm.detachCopy(obj); + obj.writeField(1000); + int curr = currentState(obj); + if( curr != DETACHED_DIRTY ) { + if (debug) { + logger.debug("StateTransition: Unable to create detached-dirty instance" + + " from a persistent-clean instance via detachCopy/persistent field modification," + + " state is " + states[curr]); + } + return null; + } + return obj; + } + } Index: src/java/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.java =================================================================== --- src/java/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.java (revision 371522) +++ src/java/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.java (working copy) @@ -19,7 +19,7 @@ import java.io.Serializable; -public class StateTransitionObj { +public class StateTransitionObj implements Serializable { private static int counter = 0; private int id; Index: src/java/org/apache/jdo/tck/JDO_Test.java =================================================================== --- src/java/org/apache/jdo/tck/JDO_Test.java (revision 371522) +++ src/java/org/apache/jdo/tck/JDO_Test.java (working copy) @@ -51,18 +51,21 @@ import org.apache.commons.logging.LogFactory; public abstract class JDO_Test extends TestCase { - public static final int TRANSIENT = 0; - public static final int PERSISTENT_NEW = 1; - public static final int PERSISTENT_CLEAN = 2; - public static final int PERSISTENT_DIRTY = 3; - public static final int HOLLOW = 4; - public static final int TRANSIENT_CLEAN = 5; - public static final int TRANSIENT_DIRTY = 6; - public static final int PERSISTENT_NEW_DELETED = 7; - public static final int PERSISTENT_DELETED = 8; - public static final int PERSISTENT_NONTRANSACTIONAL = 9; - public static final int NUM_STATES = 10; - public static final int ILLEGAL_STATE = 10; + public static final int TRANSIENT = 0; + public static final int PERSISTENT_NEW = 1; + public static final int PERSISTENT_CLEAN = 2; + public static final int PERSISTENT_DIRTY = 3; + public static final int HOLLOW = 4; + public static final int TRANSIENT_CLEAN = 5; + public static final int TRANSIENT_DIRTY = 6; + public static final int PERSISTENT_NEW_DELETED = 7; + public static final int PERSISTENT_DELETED = 8; + public static final int PERSISTENT_NONTRANSACTIONAL = 9; + public static final int PERSISTENT_NONTRANSACTIONAL_DIRTY = 10; + public static final int DETACHED_CLEAN = 11; + public static final int DETACHED_DIRTY = 12; + public static final int NUM_STATES = 13; + public static final int ILLEGAL_STATE = 13; public static final String[] states = { "transient", @@ -75,6 +78,9 @@ "persistent-new-deleted", "persistent-deleted", "persistent-nontransactional", + "persistent-nontransactional-dirty", + "detached-clean", + "detached-dirty", "illegal" }; private static final int IS_PERSISTENT = 0; @@ -82,7 +88,8 @@ private static final int IS_DIRTY = 2; private static final int IS_NEW = 3; private static final int IS_DELETED = 4; - private static final int NUM_STATUSES = 5; + private static final int IS_DETACHED = 5; + private static final int NUM_STATUSES = 6; /* * This table indicates the values returned by the status interrogation @@ -90,36 +97,45 @@ * state of an object. */ private static final boolean state_statuses[][] = { - // IS_PERSISTENT IS_TRANSACTIONAL IS_DIRTY IS_NEW IS_DELETED + // IS_PERSISTENT IS_TRANSACTIONAL IS_DIRTY IS_NEW IS_DELETED IS_DETACHED // transient - { false, false, false, false, false}, + { false, false, false, false, false, false}, // persistent-new - { true, true, true, true, false}, + { true, true, true, true, false, false}, // persistent-clean - { true, true, false, false, false}, + { true, true, false, false, false, false}, // persistent-dirty - { true, true, true, false, false}, + { true, true, true, false, false, false}, // hollow - { true, false, false, false, false}, + { true, false, false, false, false, false}, // transient-clean - { false, true, false, false, false}, + { false, true, false, false, false, false}, // transient-dirty - { false, true, true, false, false}, + { false, true, true, false, false, false}, // persistent-new-deleted - { true, true, true, true, true}, + { true, true, true, true, true, false}, // persistent-deleted - { true, true, true, false, true}, + { true, true, true, false, true, false}, // persistent-nontransactional - { true, false, false, false, false} + { true, false, false, false, false, false}, + + // persistent-nontransactional-dirty + { true, false, true, false, false, false}, + + // detached_clean + { false, false, false, false, false, true}, + + // detached_dirty + { false, false, true, false, false, true} }; /** identitytype value for applicationidentity. */ @@ -740,6 +756,10 @@ if( existingEntries ) buff.append(", "); buff.append("deleted"); } + if( JDOHelper.isDetached(o) ){ + if( existingEntries ) buff.append(", "); + buff.append("detached"); + } buff.append("}"); return buff.toString(); } @@ -749,12 +769,13 @@ */ public static int currentState(Object o) { - boolean[] status = new boolean[5]; + boolean[] status = new boolean[NUM_STATUSES]; status[IS_PERSISTENT] = JDOHelper.isPersistent(o); status[IS_TRANSACTIONAL] = JDOHelper.isTransactional(o); status[IS_DIRTY] = JDOHelper.isDirty(o); status[IS_NEW] = JDOHelper.isNew(o); status[IS_DELETED] = JDOHelper.isDeleted(o); + status[IS_DETACHED] = JDOHelper.isDetached(o); int i, j; outerloop: for( i = 0; i < NUM_STATES; ++i ){ Index: src/conf/lifecycle.conf =================================================================== --- src/conf/lifecycle.conf (revision 371522) +++ src/conf/lifecycle.conf (working copy) @@ -10,6 +10,7 @@ org.apache.jdo.tck.lifecycle.PMReturnsIdenticalInstancesForEqualObjIds \ org.apache.jdo.tck.lifecycle.PMsCanSharePCClassesButNotPCInstances \ org.apache.jdo.tck.lifecycle.StateTransitions \ +org.apache.jdo.tck.lifecycle.CheckStatesOfReturnedObjects \ org.apache.jdo.tck.lifecycle.TransientTransactionalStateCommit \ org.apache.jdo.tck.lifecycle.TransientTransactionalStateRollback \ org.apache.jdo.tck.lifecycle.nontransactional.ModificationOfNontransactionalInstanceOutsideTransaction Index: src/jdo/datastoreidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo =================================================================== --- src/jdo/datastoreidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo (revision 371522) +++ src/jdo/datastoreidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo (working copy) @@ -2,6 +2,6 @@ - + Index: src/jdo/applicationidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo =================================================================== --- src/jdo/applicationidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo (revision 371522) +++ src/jdo/applicationidentity/org/apache/jdo/tck/pc/lifecycle/StateTransitionObj.jdo (working copy) @@ -5,7 +5,8 @@ + objectid-class="org.apache.jdo.tck.pc.lifecycle.StateTransitionObj$Oid" + detachable="true">