Session's awake and sleep you mean? OK, that seems pretty safe from an unlocked access point of view.
Yes, sorry, Session's awake and sleep, not the components.
Are any other EOs faulting in prior to or while running this workflow? Are there other sessions actively processing requests at the same time?
A couple of fairly minor seeming things do happen prior to running the workflow. The application is login protected, so a fetch is done on an account table when the user logs in. I added DebugGroupDatabaseAccess to NSLog just to confirm, and it looks like the account is being fetched and three or four types of related EOs are faulted from that upon login. Nothing other than the described faulting is happening during the workflow. No other sessions are processing requests while I'm testing.
Just for giggles, have you tried adding these lines here:
_nestedEC.revert();
ec.revert();
No, you should not have to, I am just wondering if adding them changes the outcome at all.
I added them in, but got the same behavior with them as without
_nestedEC.deleteObject( _kitQuantity );
What are the delete rules on the relationships out of KitQuantity?
Both relationships (CatalogItem and InventoryItem) have Nullify as their delete rule out of KitQuantity
Also, try here:
_nestedEC.revert();
ec.revert();
Same behavior when I added these lines to the beginning of AddItemToKit.
_kitQuantity = (KitQuantity) EOUtilities.createAndInsertInstance( _nestedEC, "KitQuantity" );
Is awakeFromInsertion implemented in KitQuantity? Did you remember to call super?
KitQuantity has never had any custom methods. It's class has always been just an empty implementation.
What if you print the snapshots out in the removeItem method?
Here's the new code for removeItem:
public WOComponent removeItem() {
_nestedEC.revert();
ec.revert();
NSLog.out.appendln("Item being deleted BEFORE delete: " + ec.committedSnapshotForObject( _kitQuantity ));
_nestedEC.deleteObject( _kitQuantity );
NSArray kitQuantities = ((CatalogItem) selectedItem).kitQuantities();
for (int i = 0; i < kitQuantities.count(); i++) {
NSLog.out.appendln((i+1) + ": " + _nestedEC.committedSnapshotForObject( (KitQuantity )kitQuantities.objectAtIndex(i)));
}
_nestedEC.saveChanges();
int i = 0;
NSLog.out.appendln();
NSLog.out.appendln((i + 1) + ": " + ec.committedSnapshotForObject( _kitQuantity ));
for (; i < kitQuantities.count(); i++) {
NSLog.out.appendln((i+1) + ": " + ec.committedSnapshotForObject( (KitQuantity )kitQuantities.objectAtIndex(i)));
}
NSLog.out.appendln("ec.deletedObjects(): " + ec.deletedObjects());
ec.saveChanges();
setValueForBinding( "Changes Saved", "message" );
return context().page();
}
The output is fascinating. I'm no closer to understanding what is going on, but you are clearly asking the right questions, because I am able to identify more problems. Notice that ec's snapshot of the deleted object changes after I call saveChanges on _nestedEC (the green text is my annotation). I added a toString method to KitQuantity so that its output in ec.deletedObjects would match the output form ec.committedSnapshotForObject for identification purposes. In this test, I add then delete three KitQuantity objects:
Before removing the first item
INFO NSLog - Item being deleted BEFORE delete: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2875">; }
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
INFO NSLog - 3: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2875">; }
INFO NSLog - 4: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2873">; }
INFO NSLog - 5: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog -
INFO NSLog - Item being deleted AFTER delete: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
INFO NSLog - 3: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2873">; }
INFO NSLog - 4: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog - ec.deletedObjects(): ({quantity = 1; catalogItem = null; inventoryItem = null; })
I guess it makes sense here that the relationships would be set to null on the object shown in deletedObjects, since it had just been deleted, and Nullify is the delete rule. But is there something wrong with the fact that calling ec.committedSnapshotForObject on the same object before and after the _nestedEC's saveChanges yields different values? Now I'll remove the second item.
INFO NSLog - Item being deleted BEFORE delete: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2873">; }
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
INFO NSLog - 3: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"2873">; }
INFO NSLog - 4: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog -
INFO NSLog - Item being deleted AFTER delete: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
INFO NSLog - 3: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog - ec.deletedObjects(): ({quantity = 1; catalogItem = null; inventoryItem = null; })
Before removing the third item
INFO NSLog - Item being deleted BEFORE delete: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
INFO NSLog - 3: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"7277">; }
INFO NSLog -
INFO NSLog - Item being deleted AFTER delete: {quantity = 1; catalogItem = <com.webobjects.foundation.NSKeyValueCoding$Null>; inventoryItem =
INFO NSLog - 1: {quantity = 2; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"12362">; }
INFO NSLog - 2: {quantity = 1; catalogItem = <CatalogItem pk:"43258">; inventoryItem = <InventoryItem pk:"11642">; }
<com.webobjects.foundation.NSKeyValueCoding$Null>; }
INFO NSLog - ec.deletedObjects(): ({quantity = 1; catalogItem = null; inventoryItem = null; })
Apr 20 12:07:06 KaraokeWarehouseManager[12004] (ERXNSLogLog4jBridge.java:41) WARN NSLog - <com.webobjects.appserver._private.WOComponentRequestHandler>: Exception occurred while handling request:
java.lang.IllegalStateException: recordDeleteForObject: com.webobjects.eoaccess.EODatabaseContext com.webobjects.eoaccess.EODatabaseContext@a0fcdc failed to find a snapshot for EO with Global ID:_EOIntegralKeyGlobalID[KitQuantity (java.lang.Integer)1234] that has been deleted from er.extensions.ERXECer.extensions.ERXEC@419cf7. Cannot delete an object that has not been fetched from the database
[2007-04-20 12:07:06 EDT] <WorkerThread3> java.lang.IllegalStateException: recordDeleteForObject: com.webobjects.eoaccess.EODatabaseContext com.webobjects.eoaccess.EODatabaseContext@a0fcdc failed to find a snapshot for EO with Global ID:_EOIntegralKeyGlobalID[KitQuantity (java.lang.Integer)1234] that has been deleted from er.extensions.ERXECer.extensions.ERXEC@419cf7. Cannot delete an object that has not been fetched from the database
...
After analyzing this a million times, I think I've identified a pattern (and the problem). It always seems to die when deleting the last item that I set up (the removal it died on depended on how many items I had added when setting up the test, and I didn't realize it until now). Notice how the default ec's snapshot always points to the snapshot at the end of the array after _nestedEC saves changes. I don't know why it would do this, but it looks like it does. So when I get down to the last item, the last item snapshot is gone because it has just been deleted, hence ec's snapshot of the thing is missing. Is this ringing any bells? It seems like it ALMOST makes sense to me, but not quite. Why does the snapshot change after _nestedEC.saveChanges, and why is it pointing to the end of the relationship array? Is there some kind of off by one error in EODatabaseContext?
If I were to quit the application, restart, and come back to this page and edit this item, I could delete the KitQuantities without a care in the world. But if I immediately start deleting the NEWLY CREATED KitQuantity items, I run into problems.
That sounds very, very familiar. I am sure I have had this happen to me, but I am also pretty sure it turned out to not be an EOF bug. I am wracking my brain trying to recall when / why this happened...
It almost looks like EOF is trying to process the same delete twice in a row. Is there any chance that the action method is somehow getting called twice? I have also seen stuff like that happen when the editing contexts are not locked properly, but you seem to be locking correctly.
Pretty sure now that the action method is not getting called twice (because all the logging happens only once).
Can you print out its snapshot right then too.
See above
So it appears that the nested EC is flushing its information to the default ec correctly, and I can tell that the editingContext's snapshots are fine until very shortly before the operation. Just in case there was something funny going on in the EO's, I commented out every line of code from CatalogItem, InventoryItem, and KitQuantity (the three classes being dealt with here), so that effectively they were all blank implementations, and I still get the same result. This would seem to rule out any possibility of EOF commandment violation.
So there is nothing in them now? Are there any super classes that may be affecting things.
There was nothing in them at all last night when I tested. I went ahead and uncommented everything this morning, because it seemed to have no effect. The superclasses are just the generated classes from EOGenerator. The only unusual thing about them is that I had to change the template for the to-many relationships to make sure that they returned an empty array rather than null if no relationships had been set, because otherwise I kept getting null pointer exceptions everywhere when I tried to check the relationships. For example, the kitQuantities method of the generated _CatalogItem.java looks like this:
public NSArray kitQuantities() {
if (storedValueForKey("kitQuantities") == null) {
return new NSArray();
}
return (NSArray)storedValueForKey("kitQuantities");
}
We actually had a discussion about this a bit back on the list, and apparently it should be unnecessary, but I didn't know what else to do since I kept getting the behavior that theoretically should not happen anyway. This should not be an issue here, because all of the relationships are populated already, but for the sake of thoroughness I took out the first three lines of kitQuantities() in both _CatalogItem.java and _InventoryItem.java, and I still get the same results.
Thanks regarding the error report, and thank you so much for your help. I feel like I'm getting closer to understanding things, even if they still don't make sense. The only other thing I can think of to try is creating a brand new application with just this page and the EOModel, and don't even create custom subclasses for the EOs, but just reference them as ERXGenericRecords and use valueForKey and takeValueForKey everywhere. This would take a few hours at least, but would for sure rule out any "EOF Commandment violation" business. Does this seem worth the effort? Sometimes I get pretty discouraged when working with EOF. I realized that I've spent close to half a working week trying to do something as simple as add and delete rows for a single table, and I still don't know why it doesn't work.