You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by Jeff Awe <je...@gmail.com> on 2010/05/04 00:10:04 UTC

UnsupportedOperationException from BrokerImpl.flushTransAdditions(BrokerImpl.java:2099) using OpenJPA 1.2.2

We're running with OpenJPA 1.2.2, and seeing failures calling commit on an
EntityManager transaction.  It seems like a timing issue since we've only
seen this a few times.  It looks like BrokerImpl.flush(BrokerImpl.java:1946)
calls getTransactionalStates() to setup the Collection that the
UnsupportedOperationException eventually happens on.
getTransactionalStates() will set this to Collections.EMPTY_SET if
hasTransactionalObjects() is false.... which would cause the error we're
seeing.

Could we be doing something wrong to cause this, or is a check missing
somewhere in BrokerImpl when processing this transaction?  I'm not certain,
but I think when this fails, we're not even calling persist() on the
EntityManager.  We just open the transaction, maybe do some querying, and
then commit it.  I tried to recreate this in a standalone test, but couldn't
get it to fail.  In this test, _transAdditions is null in
BrokerImpl.flushTransAdditions(BrokerImpl.java:2096), so it returns false
right away.

2010/05/03 15:53:07.446 WARNING Action failed: Commit failed, transaction
was rolled back: updateResourceCollection(ResourceCollection)
::class.method=EntityManagerConnectionManager.tryCommit()
::thread=AEMDBServiceThreadQueue <5 of 10>
::loggername=com.ibm.sysmgmt.persistence.jpa.extensions.emcm

     org.apache.openjpa.persistence.RollbackException: null
    at
org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:523)
    at
com.ibm.sysmgmt.persistence.jpa.extensions.USMiEntityManager$USMiEntityTransaction.commit(USMiEntityManager.java:609)
    at
com.ibm.sysmgmt.persistence.jpa.extensions.EntityManagerConnectionManager.tryCommit(EntityManagerConnectionManager.java:720)
    at
com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService.tryCommit(JPAResourceCachingService.java:6536)
    at
com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService._updateResourceCollection(JPAResourceCachingService.java:3840)
    at
com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService._updateResourceCollection(JPAResourceCachingService.java:3480)
    at
com.ibm.sysmgmt.resource.caching.BaseResourceCachingService.updateResourceCollection(BaseResourceCachingService.java:4498)
    at
com.ibm.aem.common.dbservice.ResourceAccess.updateUSMIServiceDataBatch(Unknown
Source)
    at
com.ibm.aem.common.dbservice.ResourceAccess.findUSMIResourceType(Unknown
Source)
    at com.ibm.aem.common.dbservice.ResourceAccess.findResourceType(Unknown
Source)
    at com.ibm.aem.common.dbservice.ResourceAccess.queryResourceData(Unknown
Source)
    at com.ibm.aem.common.dbservice.ResourceAccess.queryResourceData(Unknown
Source)
    at com.ibm.aem.common.dbservice.AemDBService.getResourceData(Unknown
Source)
    at com.ibm.aem.common.dbservice.AemDBService.getResourceData(Unknown
Source)
    at
com.ibm.aem.common.dbservice.ManageableElementThread.manageableElementCreated(Unknown
Source)
    at com.ibm.aem.common.dbservice.ManageableElementThread.process(Unknown
Source)
    at com.tivoli.twg.libs.QueueBatch$QueueServer.run(QueueBatch.java:358)
    at java.lang.Thread.run(Thread.java:736)
Caused by:  org.apache.openjpa.persistence.PersistenceException: null
    at
org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1853)
    at
org.apache.openjpa.kernel.LocalManagedRuntime.commit(LocalManagedRuntime.java:81)
    at org.apache.openjpa.kernel.BrokerImpl.commit(BrokerImpl.java:1369)
    at
org.apache.openjpa.kernel.DelegatingBroker.commit(DelegatingBroker.java:877)
    at
org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:512)
    at
com.ibm.sysmgmt.persistence.jpa.extensions.USMiEntityManager$USMiEntityTransaction.commit(USMiEntityManager.java:604)
    ... 16 more
Caused by: java.lang.UnsupportedOperationException
    at java.util.AbstractCollection.add(AbstractCollection.java:68)
    at java.util.AbstractCollection.addAll(AbstractCollection.java:87)
    at
org.apache.openjpa.kernel.BrokerImpl.flushTransAdditions(BrokerImpl.java:2099)
    at
org.apache.openjpa.kernel.BrokerImpl.flushAdditions(BrokerImpl.java:2086)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2000)
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:1927)
    at
org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1845)
    ... 21 more

Re: UnsupportedOperationException from BrokerImpl.flushTransAdditions(BrokerImpl.java:2099) using OpenJPA 1.2.2

Posted by Jeff Awe <je...@gmail.com>.
Heath.

Thanks for the response.  In rare cases, our code is doing what you've
described, and I was able to recreate by forcing this rare case:

1.  A transaction is started and committed w/o any changes occurring.
2.  When commit is called, we have multiple TransactionListeners that are
fired, and one of them does end up making a change in rare cases.

I've coded up a workaround where we always dirty a transaction before
calling commit, but I'm a little worried about the extra overhead this would
cause.  Regardless, we're too late in our development cycle to get this
change into our product, so we'll probably have to live with this until our
next patch, where hopefully we'll be able to pull in an OpenJPA fix for
this.

I think we're in agreement that this is a valid OpenJPA bug.  Has a JIRA
issue been opened for this yet?

Thanks!

-- 
View this message in context: http://openjpa.208410.n2.nabble.com/UnsupportedOperationException-from-BrokerImpl-flushTransAdditions-BrokerImpl-java-2099-using-OpenJPA2-tp5000485p5019958.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: UnsupportedOperationException from BrokerImpl.flushTransAdditions(BrokerImpl.java:2099) using OpenJPA 1.2.2

Posted by Heath Thomann <jp...@gmail.com>.
HI!  Using the stack trace, and some particulars about the code path in your
stack, I've been able to recreate the UnsupportedOperationException.  I need
you to walk though my test (and the code if you care since you seem to have
already poked around at BrokerImpl).  Let me first summarize what my test
does, and then let me go into great detail for the rest of the folks
watching this list in order to determine if I'm on the right path.  My test
does the following (please let me know if your code does something similar):

1) My "main" code simply begins a tran, queries entities, and commits the
tran.....you stated your code does the same (i.e. I never update/dirty
anything or create new objects).
2) I've created a 'tran listener' (i.e. an impl of
org.apache.openjpa.event.TransactionListener) and in that 'listener', method
'beforeCommit' I dirty the entity queried in #1.
3) After my my 'beforeCommit' method returns, the
UnsupportedOperationException is thrown.

Does your code do anything at all remotely similar?


OK, that was the brief summary, for anyone else who cares to hear the gory
details, lets dig in.....first, the exception stack shows the exception is
hit here:

Caused by: java.lang.UnsupportedOperationException
   at java.util.AbstractCollection.add(AbstractCollection.java:68)
   at java.util.AbstractCollection.addAll(AbstractCollection.java:87)
   at
org.apache.openjpa.kernel.BrokerImpl.flushTransAdditions(BrokerImpl.java:2099)

   at
org.apache.openjpa.kernel.BrokerImpl.flushAdditions(BrokerImpl.java:2086)
   at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2000)


So, lets look at the code around 'flush(BrokerImpl.java:2000)'.  To follow
is line 2000 (the last line) and a number of lines proceeding it:


               if ((_transEventManager.hasFlushListeners()
                    || _transEventManager.hasEndListeners())
                    && (flush || reason == FLUSH_COMMIT)) {
                    // fire events
                    mobjs = new ManagedObjectCollection(transactional);
                    if (reason == FLUSH_COMMIT
                        && _transEventManager.hasEndListeners()) {
                        fireTransactionEvent(new TransactionEvent(this,
                            TransactionEvent.BEFORE_COMMIT, mobjs,
                            _persistedClss, _updatedClss, _deletedClss));

                        flushAdditions(transactional, reason);    <-----
line 2000


So, in order to get to this 'flushAdditions', you must have a 'listener'
(i.e. an impl of org.apache.openjpa.event.TransactionListener).  OK, with
that said, keep this 'listener' idea in mind as we will come back to it.

Continue to dig into the stack and going up two levels, we see that
'flushTransAdditions(BrokerImpl.java:2099)' looks like this:

    private boolean flushTransAdditions(Collection transactional, int
reason) {
        if (_transAdditions == null || _transAdditions.isEmpty())
            return false;

        // keep local transactional list copy up to date
        transactional.addAll(_transAdditions);   <----- line 2099

There are two important things to note here:
1) 'transactional' is a 'Collection'.
2) the addAll will only be called depending on the state of
'_transAdditions'.

For #1, lets visit the javadoc for Collection.addAll and see why/when it
throws the UnsupportedOperationException.....its states:

    * @throws UnsupportedOperationException if this collection does not
     *         support the <tt>addAll</tt> method.

So, we know that the 'Collection' is of a type which must not support
addAll.  This offers a clue and we should look to see at which points
'transactional' could be defined as a 'Collection' which doesn't support
'addAll'.  As you stated in your post, 'transactional' is set in BrokerImpl
at line 1946 which is here:

        Collection transactional = getTransactionalStates();

If we look at 'getTransactionalStates()', we can see that the method could
return a Collections.EMPTY_SET ('EmptySet'):

    protected Collection getTransactionalStates() {
        if (!hasTransactionalObjects())
            return Collections.EMPTY_SET;
        return _transCache.copy();
    }

An 'EmptySet.addAll' eventually calls 'AbstractCollection.add' which
blatantly throws an UnsupportedOperationException.  So, we know we must have
a case where 'transactional' is an EmtpySet.  One way this may occur, is as
you've stated, that is, that you only query objects.

Next, #2 offers another clue in that we need to look at the case where
'_transAdditions' is not null and not empty.  If we look in BorkerImpl at
the places where '_transAdditions' is set, we can see things are added to it
in the 'setDirty' method.  But, as we previously found, we are only querying
object, not making them dirty.  So, how can we have 'transactional' be an
EmptySet, yet '_transAdditions' not null or empty?  One way is to go back to
the 'listener' we discussed earlier and when the 'listener' is called, have
it dirty and entity.  In so doing, the 'setDirty' method will be called
which will add elements to '_transAdditions' such that conditions are met to
cause 'transactional.addAll' to be called in 'flushTransAdditions'.  The
ordering is basically like this:

1) 'transactional' is set to an EmptySet and the beginning of flush.
2) The 'listener' is called later on in flush which dirties an entity.  This
causes '_transAdditions' to not be null or empty.
3) After the 'listener' is called, flushTransAdditions is called where at
which time 'addAll', and then 'add', is called on an
EmptySet/AbstractCollection which returns the exception.

Thanks,

Heath

On Mon, May 3, 2010 at 4:10 PM, Jeff Awe <je...@gmail.com> wrote:

> We're running with OpenJPA 1.2.2, and seeing failures calling commit on an
> EntityManager transaction.  It seems like a timing issue since we've only
> seen this a few times.  It looks like
> BrokerImpl.flush(BrokerImpl.java:1946)
> calls getTransactionalStates() to setup the Collection that the
> UnsupportedOperationException eventually happens on.
> getTransactionalStates() will set this to Collections.EMPTY_SET if
> hasTransactionalObjects() is false.... which would cause the error we're
> seeing.
>
> Could we be doing something wrong to cause this, or is a check missing
> somewhere in BrokerImpl when processing this transaction?  I'm not certain,
> but I think when this fails, we're not even calling persist() on the
> EntityManager.  We just open the transaction, maybe do some querying, and
> then commit it.  I tried to recreate this in a standalone test, but
> couldn't
> get it to fail.  In this test, _transAdditions is null in
> BrokerImpl.flushTransAdditions(BrokerImpl.java:2096), so it returns false
> right away.
>
> 2010/05/03 15:53:07.446 WARNING Action failed: Commit failed, transaction
> was rolled back: updateResourceCollection(ResourceCollection)
> ::class.method=EntityManagerConnectionManager.tryCommit()
> ::thread=AEMDBServiceThreadQueue <5 of 10>
> ::loggername=com.ibm.sysmgmt.persistence.jpa.extensions.emcm
>
>     org.apache.openjpa.persistence.RollbackException: null
>    at
>
> org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:523)
>    at
>
> com.ibm.sysmgmt.persistence.jpa.extensions.USMiEntityManager$USMiEntityTransaction.commit(USMiEntityManager.java:609)
>    at
>
> com.ibm.sysmgmt.persistence.jpa.extensions.EntityManagerConnectionManager.tryCommit(EntityManagerConnectionManager.java:720)
>    at
>
> com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService.tryCommit(JPAResourceCachingService.java:6536)
>    at
>
> com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService._updateResourceCollection(JPAResourceCachingService.java:3840)
>    at
>
> com.ibm.sysmgmt.resource.caching.jpa.JPAResourceCachingService._updateResourceCollection(JPAResourceCachingService.java:3480)
>    at
>
> com.ibm.sysmgmt.resource.caching.BaseResourceCachingService.updateResourceCollection(BaseResourceCachingService.java:4498)
>    at
>
> com.ibm.aem.common.dbservice.ResourceAccess.updateUSMIServiceDataBatch(Unknown
> Source)
>    at
> com.ibm.aem.common.dbservice.ResourceAccess.findUSMIResourceType(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.ResourceAccess.findResourceType(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.ResourceAccess.queryResourceData(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.ResourceAccess.queryResourceData(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.AemDBService.getResourceData(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.AemDBService.getResourceData(Unknown
> Source)
>    at
>
> com.ibm.aem.common.dbservice.ManageableElementThread.manageableElementCreated(Unknown
> Source)
>    at com.ibm.aem.common.dbservice.ManageableElementThread.process(Unknown
> Source)
>    at com.tivoli.twg.libs.QueueBatch$QueueServer.run(QueueBatch.java:358)
>    at java.lang.Thread.run(Thread.java:736)
> Caused by:  org.apache.openjpa.persistence.PersistenceException: null
>    at
> org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1853)
>    at
>
> org.apache.openjpa.kernel.LocalManagedRuntime.commit(LocalManagedRuntime.java:81)
>    at org.apache.openjpa.kernel.BrokerImpl.commit(BrokerImpl.java:1369)
>    at
>
> org.apache.openjpa.kernel.DelegatingBroker.commit(DelegatingBroker.java:877)
>    at
>
> org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManagerImpl.java:512)
>    at
>
> com.ibm.sysmgmt.persistence.jpa.extensions.USMiEntityManager$USMiEntityTransaction.commit(USMiEntityManager.java:604)
>    ... 16 more
> Caused by: java.lang.UnsupportedOperationException
>    at java.util.AbstractCollection.add(AbstractCollection.java:68)
>    at java.util.AbstractCollection.addAll(AbstractCollection.java:87)
>    at
>
> org.apache.openjpa.kernel.BrokerImpl.flushTransAdditions(BrokerImpl.java:2099)
>    at
> org.apache.openjpa.kernel.BrokerImpl.flushAdditions(BrokerImpl.java:2086)
>    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2000)
>    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:1927)
>    at
> org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:1845)
>    ... 21 more
>