Objectify и TimerTask: для этого потока не зарегистрирована среда API

Я пытаюсь настроить TimerTask для периодического удаления записей из хранилища данных Google App Engine. Поэтому я установил 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);, но это не помогло.


person Charlotte Tan    schedule 12.04.2013    source источник
comment
Это не имеет ничего общего с Objectify; вы увидите ту же ошибку, если вы отправите запрос непосредственно к низкоуровневому API из вашего потока таймера. Я предлагаю вам упростить вопрос, вы можете получить лучший ответ. Я действительно удивлен, узнав, что таймеры работают в GAE (в отличие от разработки).   -  person stickfigure    schedule 12.04.2013
comment
@stickfigure Я не уверен, работают ли они в продакшене, еще не пробовал загружать. :)   -  person Charlotte Tan    schedule 13.04.2013


Ответы (1)


Из документации класса java.util.Timer:

Каждому объекту Timer соответствует один фоновый поток.

И просмотр внутреннего кода класса java.util.Timer, мы можем видеть, что он фактически создает поток, вызывая new Thread().

Между тем, из документации App Engine об использовании потоков в песочнице Java:

Вы должны использовать один из методов ThreadManager для потоки. Вы не можете вызвать new Thread() самостоятельно или использовать фабрику потоков по умолчанию.

Итак, здесь произошло то, что объект Timer создал свой собственный поток, который затем выполняет запросы Objectify, но, поскольку потоки, созданные вне ThreadManager, не имеют надлежащей настроенной для них среды API App Engine, он выдает исключение.

Вам необходимо реорганизовать свой код, чтобы избежать использования классов 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();
person Ibrahim Arief    schedule 12.04.2013
comment
Спасибо! Я попробую это. Я только что прочитал о заданиях cron, было бы разумнее попробовать этот подход? - person Charlotte Tan; 13.04.2013
comment
@choc: Cron в лучшем случае имеет гранулярность всего в минуту, и кажется, что это немного излишне для замены TimerTask. Лучше подойдет etaMillis или countdownMillis TaskQueue, но это зависит от вашего варианта использования. Если вы опубликуете свой подробный пример использования TimerTask в качестве другого вопроса StackOverflow и спросите о возможных решениях в GAE, возможно, я смогу вмешаться. - person Ibrahim Arief; 13.04.2013
comment
Спасибо! Я задал здесь новый вопрос: stackoverflow.com/ вопросы/15985309/ - person Charlotte Tan; 13.04.2013