Issue Details (XML | Word | Printable)

Key: OPENJPA-102
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Unassigned
Reporter: Patrick Linskey
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
OpenJPA

JTA transaction rollback, nonexistant instances, transactional persistence context => failures during afterCompletion() and close()

Created: 17/Jan/07 04:07 AM   Updated: 01/Mar/07 02:13 AM
Return to search
Component/s: jpa, kernel
Affects Version/s: None
Fix Version/s: 0.9.7

Time Tracking:
Not Specified

File Attachments:
  Size
Text File Licensed for inclusion in ASF works openjpa-detach.patch 2007-01-17 04:09 AM Patrick Linskey 2 kB
Environment: WebLogic Server 10.0

Resolution Date: 17/Jan/07 10:17 PM


 Description  « Hide
Configuration:
  - transactional persistence context
  - DetachState=fgs
  - JTA transactions

If an error causes the transaction manager to roll back the current transaction, BrokerImpl.afterCompletion() will be invoked with Status.STATUS_ROLLEDBACK. afterCompletion() will call BrokerImpl.free(), which will attempt to load the default fetch group. If there is an instance in the Broker's context that does not exist (that was looked up via EntityManager.getReference(), for example), then the code in free() will fail with an ObjectNotFoundException.

 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Patrick Linskey added a comment - 17/Jan/07 04:18 AM
openjpa-detach.patch solves the described issue, but exposes another issue: an ObjectNotFoundException will be thrown during a subsequent BrokerImpl.close() call. The attached patch calls free() with Status.NO_TRANSACTION if BrokerImpl.close() is invoked.

The use case at hand seems to do the following:

1. UserTransaction.begin()
2. getReference() on a nonexistant record
3. close() in a session bean
4. UserTransaction.commit(), which fails

With the patch, BrokerImpl.afterCompletion() calls BrokerImpl.free(Status.STATUS_ROLLEDBACK) from line 1789, because close had been invoked. The patch makes that free() invocation work as expected.

However, close() is being invoked later on. This later invocation is probably erroneous, but currently it is causing OpenJPA to throw an exception from BrokerImpl.java:3984, because close() calls free() with Status.NO_TRANSACTION.

I believe that OpenJPA should be short-circuiting close() if isClosed() returns true, or that free() should short-circuit if isClosed() returns true.

Patrick Linskey added a comment - 17/Jan/07 07:37 PM
Actually my description in the prior comment is incorrect. The test case does the following:

1. EntityTransaction.begin()
2. getReference() on nonexistent record
3. rollback() in a business method
4. EntityManager.close(), which fails while running the detach algorithm

IOW, short-circuiting close() won't fix the problem, as close() has only been called once. Additionally, on further inspection, EM.close() asserts that it's open before calling broker.close(), so there will never be multiple close() invocations.

The problem does not occur in a JTA context, as the persistence context is coincident with the JTA transaction in that scenario, not the lifecycle of the EntityManager.

It does not seem possible to disable the detach algorithm altogether. However, further investigation indicates that this is probably happening because OpenJPA is not clearing the persistence context during JPA rollback. From section 3.3.2 of the JPA spec:

"For both transaction-scoped and extended persistence contexts, transaction rollback causes all pre-existing
managed instances and removed instances[15] to become detached. The instances' state will be the
state of the instances at the point at which the transaction was rolled back. Transaction rollback typically
causes the persistence context to be in an inconsistent state at the point of rollback. In particular,
the state of version attributes and generated state (e.g., generated primary keys) may be inconsistent.
Instances that were formerly managed by the persistence context (including new instances that were
made persistent in that transaction) may therefore not be reusable in the same manner as other detached
objects—for example, they may fail when passed to the merge operation.[16]"

Resolving this discrepancy will address the case that I'm looking at in particular, but will not address the case where the getReference() method is invoked outside a transaction but in an extended persistence context.

Patrick Linskey added a comment - 17/Jan/07 08:24 PM
New approach: I am going to change OpenJPA's detach algorithms to handle failures more gracefully, and back out the patch I mentioned earlier. This will resolve the unexpected exceptions I was seeing.

Separately, OPENJPA-104 tracks the JPA spec discrepancy.

Patrick Linskey added a comment - 17/Jan/07 10:17 PM
Resolved with svn 497185.