I need to create a entity that can be locked which stores an incrementor given to another business logic object a unique id. I am using vanilla GAE so I don't have JPA or JDO and need to implement this with standard GAE DatastoreService logic.
Here is my first attempt at the logic public static final String PHOTO_COLLECTION_UNIQUE_ID_GENERATOR_NAME = "photoCollection"; private static final Key key = KeyFactory.createKey(UniqueIdGenerator.class.getName(), PHOTO_COLLECTION_UNIQUE_ID_GENERATOR_NAME); private static final String NEXT_ID_PROPERTY_KEY = "nextId"; public long fetchNextPhotoCollectionId(){ long nextId; Entity collectionUniqueIdGenerator = null; Transaction txn = _datastore.beginTransaction(); try { try { collectionUniqueIdGenerator = _datastore.get(key); } catch (EntityNotFoundException e) { collectionUniqueIdGenerator = new Entity(key); collectionUniqueIdGenerator.setProperty(NEXT_ID_PROPERTY_KEY, 1L); _datastore.put(collectionUniqueIdGenerator); } nextId = (Long)collectionUniqueIdGenerator.getProperty(NEXT_ID_PROPERTY_KEY); collectionUniqueIdGenerator.setProperty(NEXT_ID_PROPERTY_KEY, nextId + 1L); _datastore.put(collectionUniqueIdGenerator); } finally { if (txn.isActive()) { txn.commit(); } } return nextId; } The method is done withing a transaction and bootstraps the entity if it does not yet exist. If it does the current value is returned and the incremented value is stored for the next request. When I try and test via java Executor's service I get the error No API environment is registered for this thread. Here is my junit code, the problem is when the testfetchNextPhotoCollectionIdLockingTest method. Oh I'm using Guice and passing the DatastoreService in as a constructor parameter instead of using the factory in the class, but I had the same error when using the normal factory pattern. I'd like to test the way of creating unique ids before relying on it. But, if there is another preferred way to create them I'd like to hear about it. public class TestPhotoDAOTest { private final LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); @Before public void setUp() { helper.setUp(); } @After public void tearDown() { helper.tearDown(); } /** * Testing when no previous PhotoCollection UniqueIDGenerator existed */ @Test public void testfetchNextPhotoCollectionIdBootStrapTest(){ DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); PhotoDAO testClass = new PhotoDAO(datastore); final long nextId = testClass.fetchNextPhotoCollectionId(); Assert.assertEquals(1L, nextId); } /** * Testing that the second number generated is two */ @Test public void testfetchNextPhotoCollectionIdPreviouslyCreatedTest(){ DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); PhotoDAO testClass = new PhotoDAO(datastore); testClass.fetchNextPhotoCollectionId(); final long nextId = testClass.fetchNextPhotoCollectionId(); Assert.assertEquals(2L, nextId); } /** * Testing a random number of iteration */ @Test public void testfetchNextPhotoCollectionIdRandomIterationTest(){ DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); PhotoDAO testClass = new PhotoDAO(datastore); final long randomNumber = (long) (1000L * Math.random()); for( long i = 0; i < randomNumber; i++){ testClass.fetchNextPhotoCollectionId(); } final long nextId = testClass.fetchNextPhotoCollectionId(); Assert.assertEquals(randomNumber + 1, nextId); } @Test //This test is having problems see stack below public void testfetchNextPhotoCollectionIdLockingTest() throws Exception{ DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); final PhotoDAO instance = new PhotoDAO(datastore); ExecutorService executor = Executors.newFixedThreadPool(1); final long longIterationTarget = 10000L; FutureTask<Long> future = new FutureTask<Long>(new Callable<Long>(){ @Override public Long call() throws Exception { final long largeNumber = longIterationTarget; for( long i = 0; i < largeNumber; i++){ instance.fetchNextPhotoCollectionId(); } final long nextId = instance.fetchNextPhotoCollectionId(); return nextId; }}); executor.execute(future); final long nextId = instance.fetchNextPhotoCollectionId(); Assert.assertEquals(1L, nextId); while( !future.isDone() ){ Thread.sleep(100L); } final long longRunningId = future.get(); Assert.assertEquals(longIterationTarget + 1L, longRunningId); } } here is the stack trace the last test generates java.util.concurrent.ExecutionException: java.lang.NullPointerException: No API environment is registered for this thread. at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source) at java.util.concurrent.FutureTask.get(Unknown Source) at org.rwpid.backend.dao.TestPhotoDAOTest.testfetchNextPhotoCollectionIdLockingTest(TestPhotoDAOTest.java: 109) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.runners.model.FrameworkMethod $1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java: 15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java: 41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java: 20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java: 28) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java: 31) at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java: 79) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java: 71) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java: 49) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java: 50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java: 38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java: 467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java: 683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java: 390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java: 197) Caused by: java.lang.NullPointerException: No API environment is registered for this thread. at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java: 108) at com.google.appengine.api.datastore.BaseDatastoreServiceImpl.beginTransactionInternal(BaseDatastoreServiceImpl.java: 123) at com.google.appengine.api.datastore.DatastoreServiceImpl.beginTransaction(DatastoreServiceImpl.java: 227) at com.google.appengine.api.datastore.DatastoreServiceImpl.beginTransaction(DatastoreServiceImpl.java: 222) at org.rwpid.backend.dao.PhotoDAO.fetchNextPhotoCollectionId(PhotoDAO.java: 238) at org.rwpid.backend.dao.TestPhotoDAOTest $1.call(TestPhotoDAOTest.java:94) at org.rwpid.backend.dao.TestPhotoDAOTest $1.call(TestPhotoDAOTest.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) -- You received this message because you are subscribed to the Google Groups "Google App Engine" group. To post to this group, send email to google-appengine@googlegroups.com. To unsubscribe from this group, send email to google-appengine+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-appengine?hl=en.