Objectify和TimerTask:没有为此线程注册API环境
我正在尝试设置TimerTask
以定期从Google App Engine的dataStore中删除条目。 所以我用Timer
设置了一个ServletContextListener
。
在contextInitialized
,我已经注册了我的Objectify类:
ObjectifyService.register(Person.class);
但是,当任务实际运行时,它会抱怨没有设置API环境:
Exception in thread "Timer-0" java.lang.NullPointerException: No API environment is registered for this thread. at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java:80) at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppIdNamespace(DatastoreApiHelper.java:90) at com.google.appengine.api.datastore.Query.(Query.java:214) at com.google.appengine.api.datastore.Query.(Query.java:143) at com.googlecode.objectify.impl.cmd.QueryImpl.(QueryImpl.java:72) at com.googlecode.objectify.impl.cmd.LoadTypeImpl.createQuery(LoadTypeImpl.java:50) at com.googlecode.objectify.impl.cmd.LoadTypeImpl.filter(LoadTypeImpl.java:58) at myApp.MyServletContextListener$MyTask.run(MyServletContextListener.java:58) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505)
有任何想法吗? 我已经尝试将注册该类的行更改为ObjectifyService.factory().register(Person.class);
但它似乎没有帮助。
从java.util.Timer
类的文档 :
对应于每个Timer对象的是一个后台线程。
并且窥视java.util.Timer
类的内部代码 ,我们可以看到它基本上通过调用new Thread()
实例化线程。
同时,从App Engine的文档中了解如何在Java沙箱中使用线程:
您必须使用ThreadManager上的某个方法来创建线程。 您不能自己调用新的Thread()或使用默认的线程工厂。
所以这里发生的事情是Timer
对象实例化了他们自己的线程,然后执行Objectify查询,但由于在ThreadManager外部实例化的线程没有为它们设置适当的App Engine API环境,因此它会引发exception。
您需要重构代码以避免使用Timer和TimerTask类并改为使用基本线程。 例如,而不是使用:
import java.util.Timer; import java.util.TimerTask; ... Timer timer = new Timer(); timer.schedule( new TimerTask() { @Override public void run() { // Objectify query here. } }, 5000 );
你可以改为使用:
import com.google.appengine.api.ThreadManager; ... final long tScheduleDelay = 5000; ThreadManager.createThreadForCurrentRequest( new Runnable() { @Override public void run() { try { Thread.sleep( tScheduleDelay ); } catch ( InterruptedException ex ) { // log possible exception } // Objectify query here. } } ).start();