Return-Path: Delivered-To: apmail-openjpa-dev-archive@www.apache.org Received: (qmail 17222 invoked from network); 18 Jun 2010 01:18:47 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 18 Jun 2010 01:18:47 -0000 Received: (qmail 38174 invoked by uid 500); 18 Jun 2010 01:18:47 -0000 Delivered-To: apmail-openjpa-dev-archive@openjpa.apache.org Received: (qmail 38108 invoked by uid 500); 18 Jun 2010 01:18:46 -0000 Mailing-List: contact dev-help@openjpa.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@openjpa.apache.org Delivered-To: mailing list dev@openjpa.apache.org Received: (qmail 38100 invoked by uid 99); 18 Jun 2010 01:18:46 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 18 Jun 2010 01:18:46 +0000 X-ASF-Spam-Status: No, hits=-1528.9 required=10.0 tests=ALL_TRUSTED,AWL X-Spam-Check-By: apache.org Received: from [140.211.11.22] (HELO thor.apache.org) (140.211.11.22) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 18 Jun 2010 01:18:45 +0000 Received: from thor (localhost [127.0.0.1]) by thor.apache.org (8.13.8+Sun/8.13.8) with ESMTP id o5I1IPII018295 for ; Fri, 18 Jun 2010 01:18:25 GMT Message-ID: <27865656.71351276823905093.JavaMail.jira@thor> Date: Thu, 17 Jun 2010 21:18:25 -0400 (EDT) From: "Heath Thomann (JIRA)" To: dev@openjpa.apache.org Subject: [jira] Created: (OPENJPA-1702) UnsupportedOperationException caused in BrokerImpl during transaction commit processing. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-JIRA-FingerPrint: 30527f35849b9dde25b450d4833f0394 UnsupportedOperationException caused in BrokerImpl during transaction commi= t processing. ---------------------------------------------------------------------------= ------------- Key: OPENJPA-1702 URL: https://issues.apache.org/jira/browse/OPENJPA-1702 Project: OpenJPA Issue Type: Bug Components: kernel Affects Versions: 2.0.0, 1.2.0 Reporter: Heath Thomann Assignee: Heath Thomann Priority: Minor For a given scenario, which will be described in detail below, an Unsupport= edOperationException occurs as follows: [main] openjpa.Runtime - An exception occurred while ending the transaction= . This exception will be re-thrown. org.apache.op= enjpa.util.StoreException: null =09at org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java= :1853) =09at org.apache.openjpa.kernel.LocalManagedRuntime.commit(LocalManagedRunt= ime.java:81) =09at org.apache.openjpa.kernel.BrokerImpl.commit(BrokerImpl.java:1369) =09at org.apache.openjpa.kernel.DelegatingBroker.commit(DelegatingBroker.ja= va:877) =09at org.apache.openjpa.persistence.EntityManagerImpl.commit(EntityManager= Impl.java:513) =09at hat.tests.TestUnsupportedOp.commitTx(TestUnsupportedOp.java:44) =09at hat.tests.TestUnsupportedOp.test(TestUnsupportedOp.java:90) =09at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) =09at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.= java:39) =09at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces= sorImpl.java:25) =09at java.lang.reflect.Method.invoke(Method.java:592) =09at junit.framework.TestCase.runTest(TestCase.java:164) =09at junit.framework.TestCase.runBare(TestCase.java:130) =09at junit.framework.TestResult$1.protect(TestResult.java:110) =09at junit.framework.TestResult.runProtected(TestResult.java:128) =09at junit.framework.TestResult.run(TestResult.java:113) =09at junit.framework.TestCase.run(TestCase.java:120) =09at junit.framework.TestSuite.runTest(TestSuite.java:228) =09at junit.framework.TestSuite.run(TestSuite.java:223) =09at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.= java:35) =09at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4= TestReference.java:46) =09at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution= .java:38) =09at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(Remot= eTestRunner.java:467) =09at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(Remot= eTestRunner.java:683) =09at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTest= Runner.java:390) =09at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTes= tRunner.java:197) Caused by: java.lang.UnsupportedOperationException =09at java.util.AbstractCollection.add(AbstractCollection.java:216) =09at java.util.AbstractCollection.addAll(AbstractCollection.java:318) =09at org.apache.openjpa.kernel.BrokerImpl.flushTransAdditions(BrokerImpl.j= ava:2103) =09at org.apache.openjpa.kernel.BrokerImpl.flushAdditions(BrokerImpl.java:2= 086) =09at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2000) =09at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:1927) =09at org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java= :1845) =09... 25 more Using the stack trace, and some particulars about the code path, I've been = able to recreate the UnsupportedOperationException. =C2=A0Let me first summ= arize what my test does, and then let me go into great details on how the i= ssue occurs.=C2=A0 My test does the following: 1) My "main" code simply begins a tran, performs a query, and commits the t= ran. 2) I've created a 'tran listener' (i.e. an impl of org.apache.openjpa.event= .TransactionListener) and in that 'listener', method 'beforeCommit', I dirt= y the entity queried/found in #1. 3) After my 'beforeCommit' method returns, the UnsupportedOperationExceptio= n is thrown. 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 =C2=A0 =C2=A0at java.util.AbstractCollection.add(AbstractCollection.java:68= ) =C2=A0 =C2=A0at java.util.AbstractCollection.addAll(AbstractCollection.java= :87) =C2=A0 =C2=A0at org.apache.openjpa.kernel.BrokerImpl.flushTransAdditions(BrokerImpl.java:20= 99)=20 =C2=A0 =C2=A0at org.apache.openjpa.kernel.BrokerImpl.flushAdditions(BrokerImpl.java:2086) =C2=A0 =C2=A0at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:= 2000)=20 So, lets look at the code around 'flush(BrokerImpl.java:2000)'.=C2=A0 To fo= llow is line 2000 (the last line) and a number of lines proceeding it: =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0 if ((_transEventManager.hasFlushListeners() =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 || _transEventManager.hasEndListene= rs()) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 && (flush || reason =3D=3D FLUSH_CO= MMIT)) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // fire events =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mobjs =3D new ManagedObjectCollecti= on(transactional); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (reason =3D=3D FLUSH_COMMIT =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 && _transEv= entManager.hasEndListeners()) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fireTransac= tionEvent(new TransactionEvent(this,=20 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0 TransactionEvent.BEFORE_COMMIT, mobjs, =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0 _persistedClss, _updatedClss, _deletedClss)); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 flushAdditi= ons(transactional, reason);=C2=A0=C2=A0=C2=A0 <----- 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).=C2=A0 OK, wit= h 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 'flushT= ransAdditions(BrokerImpl.java:2099)' looks like this: =C2=A0=C2=A0=C2=A0 private boolean flushTransAdditions(Collection transacti= onal, int reason) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (_transAdditions =3D=3D null = || _transAdditions.isEmpty()) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return f= alse; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // keep local transactional list= copy up to date =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 transactional.addAll(_transAddit= ions);=C2=A0=C2=A0 <----- 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 '_transAddition= s'. For #1, lets visit the javadoc for Collection.addAll and see why/when it th= rows the UnsupportedOperationException.....its states: =C2=A0=C2=A0=C2=A0 * @throws UnsupportedOperationException if this collecti= on does not =C2=A0=C2=A0=C2=A0=C2=A0 *=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 = support the addAll method. So, we know that the 'Collection' is of a type which must not support addAl= l.=C2=A0 This offers a clue and we should look to see at which points 'tran= sactional' could be defined as a 'Collection' which doesn't support 'addAll= '.=C2=A0 'transactional' is set in BrokerImpl at line 1946 which is here: =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Collection transactional =3D get= TransactionalStates(); If we look at 'getTransactionalStates()', we can see that the method could = return a Collections.EMPTY_SET ('EmptySet'): =C2=A0=C2=A0=C2=A0 protected Collection getTransactionalStates() { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (!hasTransactionalObjects()) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return C= ollections.EMPTY_SET; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return _transCache.copy(); =C2=A0=C2=A0=C2=A0 } An 'EmptySet.addAll' eventually calls 'AbstractCollection.add' which blatan= tly throws an UnsupportedOperationException (plus, and Collections.EMPTY_SE= T is immutable, so we should be adding to it anyway).=C2=A0 So, we know we = must have a case where 'transactional' is an EmtpySet.=C2=A0 One way this m= ay occur is to only query objects as I've done in step #1 of my test (i.e. = I never dirty anything in step #1). Next, #2 offers another clue in that we need to look at the case where '_tr= ansAdditions' is not null and not empty.=C2=A0 If we look in BorkerImpl at = the places where '_transAdditions' is set, we can see things are added to i= t in the 'setDirty' method.=C2=A0 But, as we previously found, we are only = querying objects, not making them dirty.=C2=A0 So, how can we have 'transac= tional' be an EmptySet, yet '_transAdditions' not null or empty?=C2=A0 One = way is to go back to the 'listener' we discussed earlier and when the 'list= ener' is called, have it dirty an entity.=C2=A0 In so doing, the 'setDirty'= method will be called which will add elements to '_transAdditions' such th= at conditions are met to cause 'transactional.addAll' to be called in 'flus= hTransAdditions'.=C2=A0 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.=C2= =A0 This causes '_transAdditions' to not be null or empty. 3) After the 'listener' is called, flushTransAdditions is called where at w= hich time 'addAll', and then 'add', is called on an EmptySet/AbstractCollec= tion which returns the exception. --=20 This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.