Objectify和TimerTask:没有为此线程注册API环境

时间:2013-04-12 16:26:38

标签: java google-app-engine scheduled-tasks objectify servletcontextlistener

我正在尝试设置TimerTask以定期从Google App Engine的dataStore中删除条目。所以我设置ServletContextListener Timer

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.<init>(Query.java:214)
    at com.google.appengine.api.datastore.Query.<init>(Query.java:143)
    at com.googlecode.objectify.impl.cmd.QueryImpl.<init>(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);,但似乎没有帮助。

1 个答案:

答案 0 :(得分:6)

来自documentation of java.util.Timer class

  

对应每个Timer对象是一个后台线程。

而且peeking to the inner code of the java.util.Timer class,我们可以看到它基本上通过调用new Thread()来实例化线程。

同时,从App Engine's documentation开始,关于在他们的Java沙箱中使用线程:

  

您必须使用ThreadManager上的某个方法来创建线程。 您不能自己调用​​新的Thread()或使用默认的线程工厂。

所以这里发生的是Timer对象实例化自己的线程,然后执行Objectify查询,但由于在ThreadManager外部实例化的线程没有为它们设置适当的App Engine API环境,它会抛出一个异常。

您需要重构代码以避免使用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();