5

Sto tentando di ottenere un TimerTask impostato per rimuovere periodicamente le voci dal dataStore di Google App Engine. Così ho creato uno ServletContextListener con uno Timer.Objectify e TimerTask: Nessun ambiente API è registrato per questo thread

All'interno del contextInitialized, ho registrato le mie classi oggettivare:

ObjectifyService.register(Person.class); 

Tuttavia, quando l'attività viene eseguita in realtà, si lamenta del fatto che nessun ambiente di API è stato istituito:

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) 

Tutte le idee ? Ho provato a cambiare la linea che registra la classe su ObjectifyService.factory().register(Person.class); ma non sembra essere di aiuto.

+1

Questo non ha nulla a che fare con oggettivare; vedrai lo stesso errore se invii una query direttamente all'API di basso livello dal tuo thread del timer. Ti suggerisco di semplificare la domanda, potresti ottenere una risposta migliore. Sono davvero sorpreso di sentire che i timer funzionano nella produzione di GAE (al contrario di dev). – stickfigure

+0

@stickfigure Non sono sicuro che funzionino in produzione, non abbiamo ancora provato a caricarlo. :) –

risposta

6

Dal documentation of java.util.Timer class:

corrispondente a ciascun oggetto Timer è un singolo filo di fondo.

E peeking to the inner code of the java.util.Timer class, possiamo vedere che esso crea un'istanza sostanzialmente il filo invocando new Thread().

Nel frattempo, dal App Engine's documentation circa l'uso di fili nella loro sandbox Java:

è necessario utilizzare uno dei metodi su ThreadManager per creare i thread. Non è possibile richiamare la nuova discussione() o utilizzare la produzione di thread predefinita.

Quindi, ciò che è accaduto qui è l'oggetto Timer un'istanza proprio filo, che poi esegue le query oggettivare, ma dal momento che le discussioni istanziati fuori ThreadManager non ha il giusto ambiente API App Engine istituito per loro, viene generata un'eccezione .

È necessario effettuare il refactoring del codice per evitare l'utilizzo delle classi Timer e TimerTask e utilizzare invece thread di base. Ad esempio, invece di utilizzare:

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

... 

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

Si potrebbe invece utilizzare:

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

Grazie! Ci proverò. Ho appena letto di cron jobs, avrebbe più senso provare questo approccio? –

+0

@choc: I cron hanno solo la granularità di un minuto nella migliore delle ipotesi e sembra essere un po 'eccessivo per la sostituzione di TimerTask. TaskQueue [etaMillis o countdownMillis] (https://developers.google.com/appengine/docs/java/taskqueue/overview-push#The_Rate_of_Task_Execution) potrebbe essere una soluzione migliore, ma dipende dal tuo caso d'uso. Se pubblichi il caso d'uso dettagliato di TimerTask come una domanda StackOverflow diversa e chiedi possibili soluzioni in GAE, potrei essere in grado di intervenire. –

+0

Grazie! Ho posto una nuova domanda qui: http://stackoverflow.com/questions/15985309/periodically-delete-entries-in-objectify –

Problemi correlati