5

Ich versuche, eine TimerTask-Einstellung einzurichten, um Einträge aus dem Google App Engine-DataStore in regelmäßigen Abständen zu entfernen. Also habe ich einen ServletContextListener mit einem Timer eingerichtet.Objectify und TimerTask: Für diesen Thread ist keine API-Umgebung registriert.

Im Inneren des contextInitialized, ich habe meine Objectify Klassen angemeldet:

ObjectifyService.register(Person.class); 

Wenn jedoch die Aufgabe tatsächlich läuft, wirft sie keine API-Umgebung eingerichtet wurde:

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) 

Irgendwelche Ideen ? Ich habe versucht, die Zeile zu ändern, die die Klasse auf ObjectifyService.factory().register(Person.class); registriert, aber es schien nicht zu helfen.

+1

Das hat nichts mit Objectify zu tun hat; Sie werden den gleichen Fehler sehen, wenn Sie eine Anfrage direkt von Ihrem Timer-Thread an die Low-Level-API senden. Ich schlage vor, Sie vereinfachen die Frage, Sie können eine bessere Antwort bekommen. Ich bin wirklich überrascht zu hören, dass Timer in der GAE-Produktion arbeiten (im Gegensatz zum Entwickler). – stickfigure

+0

@stickfigure Ich bin mir nicht sicher, ob sie in der Produktion arbeiten, noch nicht versucht haben, sie hochzuladen. :) –

Antwort

6

Vom documentation of java.util.Timer class:

zu jedem Timer-Objekt entspricht, ist ein einzelner Hintergrund-Thread.

Und peeking to the inner code of the java.util.Timer class, können wir sehen, dass es im Grunde den Faden new Thread() durch den Aufruf instanziiert.

Zwischenzeit von App Engine's documentation über die Verwendung von Threads in ihrer Java-Sandbox:

Sie eine der Methoden auf ThreadManager verwenden müssen Threads erstellen. Sie können nicht neue Thread() selbst aufrufen oder verwenden Sie die Standard-Thread-Factory.

Also, was hier passiert ist, ist das Timer Objekt ihrer eigenen Thread instanziiert, der dann die Objectify Abfragen ausführt, aber da Threads außerhalb Thread instanziiert nicht das richtige für sie einrichten App Engine API-Umgebung haben, wirft es eine Ausnahme .

Sie müssen Ihren Code umgestalten, um die Verwendung der Timer- und TimerTask-Klassen zu vermeiden und stattdessen einfache Threads zu verwenden. Zum Beispiel, anstelle der Verwendung:

import java.util.Timer; 
import java.util.TimerTask; 

... 

Timer timer = new Timer(); 
timer.schedule(new TimerTask() 
{ 
    @Override 
    public void run() 
    { 
     // Objectify query here. 
    } 
}, 5000); 

könnten Sie stattdessen verwenden:

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(); 
+0

Danke! Ich werde es versuchen. Ich habe gerade über Cron-Jobs gelesen, wäre es sinnvoller, stattdessen diesen Ansatz zu versuchen? –

+0

@choc: Crons haben bestenfalls eine Granularität von höchstens einer Minute und scheinen beim TimerTask-Ersatz etwas übertrieben zu sein. TaskQueues [etaMillis oder countdownMillis] (https://developers.google.com/appengine/docs/java/taskqueue/overview-push#The_Rate_of_Task_Execution) ist möglicherweise besser geeignet, hängt jedoch von Ihrem Anwendungsfall ab. Wenn Sie Ihren detaillierten TimerTask-Anwendungsfall als eine andere StackOverflow-Frage posten und nach möglichen Lösungen in GAE fragen, kann ich möglicherweise einspringen. –

+0

Vielen Dank! Ich habe hier eine neue Frage gestellt: http://stackoverflow.com/questions/15985309/periodically-delete-entries-in-objectify –