PageRenderTime 65ms CodeModel.GetById 9ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/com/google/appengine/datanucleus/jpa/JPAConcurrentModificationTest.java

http://datanucleus-appengine.googlecode.com/
Java | 496 lines | 407 code | 46 blank | 43 comment | 10 complexity | 393b245c67f4b17998df6ba42af1a8fe MD5 | raw file
  1/*
  2 * /**********************************************************************
  3 * Copyright (c) 2009 Google Inc.
  4 *
  5 * Licensed under the Apache License, Version 2.0 (the "License");
  6 * you may not use this file except in compliance with the License.
  7 * You may obtain a copy of the License at
  8 *
  9 * http://www.apache.org/licenses/LICENSE-2.0
 10 *
 11 * Unless required by applicable law or agreed to in writing, software
 12 * distributed under the License is distributed on an "AS IS" BASIS,
 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14 * See the License for the specific language governing permissions and
 15 * limitations under the License.
 16 * **********************************************************************/
 17package com.google.appengine.datanucleus.jpa;
 18
 19import com.google.appengine.api.datastore.Entity;
 20import com.google.appengine.datanucleus.CollisionDatastoreDelegate;
 21import com.google.appengine.datanucleus.ExceptionThrowingDatastoreDelegate;
 22import com.google.appengine.datanucleus.Inner;
 23import com.google.appengine.datanucleus.test.jpa.Book;
 24
 25import java.util.ConcurrentModificationException;
 26
 27import javax.persistence.PersistenceException;
 28import javax.persistence.RollbackException;
 29
 30import org.datanucleus.exceptions.NucleusDataStoreException;
 31
 32/**
 33 * @author Max Ross <maxr@google.com>
 34 */
 35@Inner
 36public class JPAConcurrentModificationTest extends JPATestCase {
 37
 38  public void testInsertCollides() {
 39    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
 40    setDelegateForThread(dd);
 41    try {
 42      Book book = new Book();
 43      book.setAuthor("harold");
 44      book.setIsbn("1234");
 45      book.setFirstPublished(1888);
 46      book.setTitle("the title");
 47      beginTxn();
 48      try {
 49        em.persist(book);
 50        commitTxn();
 51        fail("expected exception");
 52      } catch (RollbackException e) {
 53        // good
 54        assertTrue(e.getCause() instanceof PersistenceException);
 55        assertTrue(e.getCause().getCause() instanceof ConcurrentModificationException);
 56      }
 57      assertFalse(em.getTransaction().isActive());
 58      assertEquals(book, "harold", "1234", 1888, "the title");
 59    } finally {
 60      setDelegateForThread(dd.getInner());
 61    }
 62  }
 63
 64  public void testInsertCollidesOnCommit() {
 65    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
 66        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
 67          int count = 0;
 68          protected void doIntercept(String methodName) {
 69            if (count != 0) {
 70              throw new ConcurrentModificationException();
 71            }
 72            count++;
 73          }
 74        };
 75    ExceptionThrowingDatastoreDelegate dd =
 76        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
 77    setDelegateForThread(dd);
 78    try {
 79      Book book = new Book();
 80      book.setAuthor("harold");
 81      book.setIsbn("1234");
 82      book.setFirstPublished(1888);
 83      book.setTitle("the title");
 84      beginTxn();
 85      try {
 86        em.persist(book);
 87        commitTxn();
 88        fail("expected exception");
 89      } catch (RollbackException e) {
 90        // good
 91        assertTrue(e.getCause() instanceof PersistenceException);
 92        assertTrue(e.getCause().getCause() instanceof NucleusDataStoreException);
 93        assertTrue(e.getCause().getCause().getCause() instanceof ConcurrentModificationException);
 94      }
 95      assertFalse(em.getTransaction().isActive());
 96      assertEquals(book, "harold", "1234", 1888, "the title");
 97    } finally {
 98      setDelegateForThread(dd.getInner());
 99    }
100  }
101
102  public void testUpdateCollides() {
103    Entity e = Book.newBookEntity("harold", "1234", "the title");
104    ds.put(e);
105    CollisionDatastoreDelegate dd =
106        new CollisionDatastoreDelegate(getDelegateForThread());
107    setDelegateForThread(dd);
108    try {
109      beginTxn();
110      Book b = em.find(Book.class, e.getKey());
111      try {
112        b.setFirstPublished(1998);
113        commitTxn();
114        fail("expected exception");
115      } catch (RollbackException ex) {
116        // good
117        assertTrue(ex.getCause() instanceof PersistenceException);
118        assertTrue(ex.getCause().getCause() instanceof ConcurrentModificationException);
119      }
120      assertFalse(em.getTransaction().isActive());
121      assertEquals(b, "harold", "1234", 2000, "the title");
122    } finally {
123      setDelegateForThread(dd.getInner());
124    }
125  }
126
127  public void testUpdateOfDetachedCollides() {
128    Entity e = Book.newBookEntity("harold", "1234", "the title");
129    ds.put(e);
130    beginTxn();
131    Book book = em.find(Book.class, e.getKey());
132    commitTxn();
133
134    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
135    setDelegateForThread(dd);
136
137    try {
138      try {
139        // update detached object TODO The object here is not "detached" at all. It is HOLLOW
140        book.setFirstPublished(1988);
141        beginTxn();
142
143        // reattach
144        em.merge(book);
145        commitTxn();
146        fail("expected exception");
147      } catch (RollbackException ex) {
148        // good
149        assertTrue(ex.getCause() instanceof PersistenceException);
150        assertTrue(ex.getCause().getCause() instanceof ConcurrentModificationException);
151      } catch (NucleusDataStoreException nde) {
152        assertTrue(nde.getCause() instanceof ConcurrentModificationException);
153      }
154      assertFalse(em.getTransaction().isActive());
155      // now verify that the new value is still in the detached version.
156      assertEquals(book, "harold", "1234", 1988, "the title");
157    } finally {
158      setDelegateForThread(dd.getInner());
159    }
160  }
161
162  public void testUpdateOfDetachedCollidesThenSucceeds() {
163
164    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
165        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
166          int count = 0;
167          protected void doIntercept(String methodName) {
168            if (count == 0) {
169              count++;
170              throw new ConcurrentModificationException();
171            }
172          }
173        };
174
175    Entity e = Book.newBookEntity("harold", "1234", "the title");
176    ds.put(e);
177    beginTxn();
178    Book book = em.find(Book.class, e.getKey());
179    commitTxn();
180
181    ExceptionThrowingDatastoreDelegate dd =
182        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
183    setDelegateForThread(dd);
184
185    try {
186      try {
187        // update detached object TODO The object here is not "detached" at all. It is HOLLOW
188        book.setFirstPublished(1988);
189        beginTxn();
190
191        // reattach
192        em.merge(book);
193        commitTxn();
194        fail("expected exception");
195      } catch (RollbackException ex) {
196        // good
197        assertTrue(ex.getCause() instanceof PersistenceException);
198        assertTrue(ex.getCause().getCause() instanceof ConcurrentModificationException);
199      } catch (NucleusDataStoreException nde) {
200        assertTrue(nde.getCause() instanceof ConcurrentModificationException);
201      }
202
203      assertFalse(em.getTransaction().isActive());
204      beginTxn();
205      book.setFirstPublished(1989);
206      em.merge(book);
207      commitTxn();
208      beginTxn();
209      book = em.find(Book.class, e.getKey());
210      assertEquals(book, "harold", "1234", 1989, "the title");
211      commitTxn();
212    } finally {
213      setDelegateForThread(dd.getInner());
214    }
215  }
216
217  public void testUpdateOfAttachedCollidesThenSucceeds() {
218
219    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
220        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
221          int count = 0;
222          protected void doIntercept(String methodName) {
223            if (count == 0) {
224              count++;
225              throw new ConcurrentModificationException();
226            }
227          }
228        };
229
230    Entity e = Book.newBookEntity("harold", "1234", "the title");
231    ds.put(e);
232    beginTxn();
233    Book b = em.find(Book.class, e.getKey());
234    ExceptionThrowingDatastoreDelegate dd =
235        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
236    setDelegateForThread(dd);
237
238    try {
239      try {
240        // update attached object TODO The object here is not "detached" at all. It is HOLLOW
241        b.setFirstPublished(1988);
242        commitTxn();
243        fail("expected exception");
244      } catch (RollbackException ex) {
245        // good
246        assertTrue(ex.getCause() instanceof PersistenceException);
247        assertTrue(ex.getCause().getCause() instanceof ConcurrentModificationException);
248      }
249      // rollback of txn causes state of pojo to rollback as well
250      assertEquals(2000, b.getFirstPublished());
251      assertFalse(em.getTransaction().isActive());
252      beginTxn();
253      // reapply the change
254      b.setFirstPublished(1988);
255      em.merge(b);
256      commitTxn();
257      beginTxn();
258      b = em.find(Book.class, e.getKey());
259      assertEquals(b, "harold", "1234", 1988, "the title");
260      commitTxn();
261    } finally {
262      setDelegateForThread(dd.getInner());
263    }
264  }
265
266  public void testDeleteCollides() {
267    Entity e = Book.newBookEntity("harold", "1234", "the title");
268    ds.put(e);
269    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
270    setDelegateForThread(dd);
271
272    try {
273      beginTxn();
274      Book b = em.find(Book.class, e.getKey());
275      em.remove(b);
276
277      try {
278        commitTxn();
279        fail("expected exception");
280      } catch (RollbackException ex) {
281        // good
282        assertTrue(ex.getCause() instanceof PersistenceException);
283        assertTrue(ex.getCause().getCause() instanceof ConcurrentModificationException);
284      }
285    } finally {
286      setDelegateForThread(dd.getInner());
287    }
288  }
289
290  public void testInsertCollides_NoTxn() {
291    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
292    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
293    setDelegateForThread(dd);
294    try {
295      Book book = new Book();
296      book.setAuthor("harold");
297      book.setIsbn("1234");
298      book.setFirstPublished(1988);
299      book.setTitle("the title");
300      try {
301        em.persist(book);
302        em.close();
303        fail("expected exception");
304      } catch (PersistenceException e) {
305        assertTrue(e.getCause() instanceof ConcurrentModificationException);
306      }
307      assertEquals(book, "harold", "1234", 1988, "the title");
308    } finally {
309      setDelegateForThread(dd.getInner());
310    }
311  }
312
313  public void testUpdateCollides_NoTxn() {
314    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
315    Entity e = Book.newBookEntity("harold", "1234", "the title");
316    ds.put(e);
317    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
318    setDelegateForThread(dd);
319
320    try {
321      Book b = em.find(Book.class, e.getKey());
322      try {
323        b.setFirstPublished(1988);
324        em.close();
325        fail("expected exception");
326      } catch (PersistenceException ex) {
327        assertTrue(ex.getCause() instanceof ConcurrentModificationException);
328      } catch (NucleusDataStoreException nde) {
329        assertTrue(nde.getCause() instanceof ConcurrentModificationException);
330      }
331      assertEquals(b, "harold", "1234", 1988, "the title");
332    } finally {
333      setDelegateForThread(dd.getInner());
334    }
335  }
336
337  public void testUpdateOfDetachedCollides_NoTxn() {
338    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
339    Entity e = Book.newBookEntity("harold", "1234", "the title");
340    ds.put(e);
341    // DataNuc JPA impl won't detach the object unless you open and close a txn ... like the JPA spec says
342    beginTxn();
343    Book book = em.find(Book.class, e.getKey());
344    commitTxn();
345    em.close();
346    em = emf.createEntityManager();
347
348    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
349    setDelegateForThread(dd);
350
351    try {
352      // update detached object
353      book.setFirstPublished(1988);
354
355      try {
356        // reattach
357        em.merge(book);
358        em.close();
359        fail("expected exception");
360      } catch (PersistenceException ex) {
361        assertTrue(ex.getCause() instanceof ConcurrentModificationException);
362      }
363      // now verify that the new value is still in the detached version.
364      assertEquals(book, "harold", "1234", 1988, "the title");
365    } finally {
366      setDelegateForThread(dd.getInner());
367    }
368  }
369
370  public void testUpdateOfDetachedCollidesThenSucceeds_NoTxn() {
371    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
372
373    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
374        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
375          int count = 0;
376          protected void doIntercept(String methodName) {
377            if (count == 0) {
378              count++;
379              throw new ConcurrentModificationException();
380            }
381          }
382        };
383
384    Entity e = Book.newBookEntity("harold", "1234", "the title");
385    ds.put(e);
386    beginTxn();
387    Book b = em.find(Book.class, e.getKey());
388    commitTxn();
389    em.close();
390    em = emf.createEntityManager();
391    ExceptionThrowingDatastoreDelegate dd =
392        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
393    setDelegateForThread(dd);
394
395    try {
396      // update detached object
397      b.setFirstPublished(1988);
398
399      // reattach
400      try {
401        em.merge(b);
402        em.close();
403        fail("expected exception");
404      } catch (PersistenceException ex) {
405        assertTrue(ex.getCause() instanceof ConcurrentModificationException);
406      }
407      em = emf.createEntityManager();
408      em.merge(b);
409      em.close();
410      em = emf.createEntityManager();
411      b = em.find(Book.class, e.getKey());
412      assertEquals(b, "harold", "1234", 1988, "the title");
413      em.close();
414    } finally {
415      setDelegateForThread(dd.getInner());
416    }
417  }
418
419  public void testUpdateOfAttachedCollidesThenSucceeds_NoTxn() {
420    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
421
422    ExceptionThrowingDatastoreDelegate.ExceptionPolicy policy =
423        new ExceptionThrowingDatastoreDelegate.BaseExceptionPolicy() {
424          int count = 0;
425          protected void doIntercept(String methodName) {
426            if (count == 0) {
427              count++;
428              throw new ConcurrentModificationException();
429            }
430          }
431        };
432
433    Entity e = Book.newBookEntity("harold", "1234", "the title");
434    ds.put(e);
435    Book b = em.find(Book.class, e.getKey());
436    // make a copy right away, otherwise our change will get reverted when the txn rolls back
437    ExceptionThrowingDatastoreDelegate dd =
438        new ExceptionThrowingDatastoreDelegate(getDelegateForThread(), policy);
439    setDelegateForThread(dd);
440
441    try {
442      // update attached object
443      try {
444        b.setFirstPublished(1988);
445        em.merge(b);
446        em.close();
447        fail("expected exception");
448      } catch (PersistenceException ex) {
449        assertTrue(ex.getCause() instanceof ConcurrentModificationException);
450      } catch (NucleusDataStoreException nde) {
451        assertTrue(nde.getCause() instanceof ConcurrentModificationException);
452      }
453      em = emf.createEntityManager();
454      b = em.find(Book.class, e.getKey());
455      // update attached object
456      b.setFirstPublished(1988);
457      em.merge(b);
458      em.close();
459      em = emf.createEntityManager();
460      b = em.find(Book.class, e.getKey());
461      assertEquals(b, "harold", "1234", 1988, "the title");
462      em.close();
463    } finally {
464      setDelegateForThread(dd.getInner());
465    }
466  }
467
468  public void testDeleteCollides_NoTxn() {
469    switchDatasource(EntityManagerFactoryName.nontransactional_ds_non_transactional_ops_allowed);
470
471    Entity e = Book.newBookEntity("harold", "1234", "the title");
472    ds.put(e);
473    CollisionDatastoreDelegate dd = new CollisionDatastoreDelegate(getDelegateForThread());
474    setDelegateForThread(dd);
475
476    try {
477      Book b = em.find(Book.class, e.getKey());
478      try {
479        em.remove(b);
480        em.close();
481        fail("expected exception");
482      } catch (PersistenceException ex) {
483        assertTrue(ex.getCause() instanceof ConcurrentModificationException);
484      }
485    } finally {
486      setDelegateForThread(dd.getInner());
487    }
488  }
489
490  private void assertEquals(Book book, String author, String isbn, int firstPublished, String title) {
491    assertEquals(author, book.getAuthor());
492    assertEquals(isbn, book.getIsbn());
493    assertEquals(firstPublished, book.getFirstPublished());
494    assertEquals(title, book.getTitle());
495  }
496}