2012-02-07 6 views
20

Ho un file WAR distribuito sul server Tomcat, una delle classi verrà richiamata all'avvio, quindi il metodo init() pianificherà un timer che si attiva ogni 5 ore per eseguire alcune attività .Arresta il timer programmato all'arresto del telefono tomcat

codice

mio init() è simile al seguente:

public void init() 
{ 
    TimerTask parserTimerTask = new TimerTask() { 

     @Override 
     public void run() { 
      XmlParser.parsePage(); 
     } 
    }; 

    Timer parserTimer = new Timer(); 
    parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD); 
} 

La mia applicazione funziona senza problemi, ma quando ho spegnere il Tomcat utilizzando /etc/init.d/tomcat7 fermo, poi controllare il registro (catalina.out) ha una voce come questa:

SEVERE: L'applicazione Web [/ MyApplication] sembra aver avviato un thread chiamato [Timer-0] ma non è riuscito a fermarlo. È molto probabile che questo crei una perdita di memoria.

posso capire che questo è causato da me programmare il timer, ma la mia domanda è:

  1. non ho impostato setDeamon su true, quindi non dovrebbe impedire che il timer Tomcat di arrestare, piuttosto che lasciato in esecuzione?
  2. Posso, nella mia applicazione, rilevare che Tomcat sta per essere spento e annullare il mio timer?
  3. Quali sono le altre soluzioni che posso utilizzare per risolvere questo problema?

Grazie!

UPDATE

ho cambiato il mio codice di seguito sulla base di alcuni di ricerca e la risposta di DaveHowes.

Timer parserTimer; 
TimerTask parserTimerTask; 

public void init() 
{ 
    parserTimerTask = new TimerTask() { 

     @Override 
     public void run() { 
      XmlParser.parsePage(); 
     } 
    }; 

    parserTimer = new Timer(); 
    parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD); 
} 

@Override 
public void contextDestroyed(ServletContextEvent arg0) { 
    Logger logger = Logger.getRootLogger(); 
    logger.info("DETECT TOMCAT SERVER IS GOING TO SHUT DOWN"); 
    logger.info("CANCEL TIMER TASK AND TIMER"); 

    otsParserTimerTask.cancel(); 

    otsParserTimer.cancel(); 

    logger.info("CANCELING COMPLETE"); 
} 

@Override 
public void contextInitialized(ServletContextEvent arg0) { 

} 

Ora la mia nuova domanda:

  1. annullo TimerTask prima quindi Timer, è corretto?
  2. Ci sono altre cose che dovrei fare?

Grazie!

UPDATE

Non funziona. Inserisco alcune istruzioni di registrazione nel metodo contextDestroyed(), dopo aver spento Tomcat, il file di registro ha solo il seguente:

PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:45) :: DETECT TOMCAT server sta per CHIUDERE PowderGodAppWebService -> [7 Feb 2012 16:09:46] INFO (PowderGodAppWebService.java:46) :: CANCELLA TIMER COMPITO E TIMER

Annullamento COMPLETO è non là.

Ho controllato anche i processi in esecuzione (non sono un esperto di Linux, quindi utilizzo semplicemente Activity Monitor di Mac.

  • Assicurarsi che nessun processo di Java è in esecuzione
  • Avviare Tomcat, si noti il ​​PID di quel processo java
  • arresto Tomcat
  • Trovato il processo di Tomcat è andato
  • Avviare Tomcat, si noti il ​​PID quel processo java
  • Distribuire il mio file di guerra
  • campione del processo, si veda [Timer-0] filo c'è
  • Shutdown Tomcat
  • Trovato che il processo è ancora lì
  • Campione del processo
  • Sede [Timer-0] è ancora lì

FISSO

ho cambiato il mio codice per parserTimer = new Timer(true); in modo che il mio timer venga eseguito come thread daemon perché contextDestroyed() viene chiamato dopo l'arresto effettivo di Tomcat.

"Tutti i servlet e i filtri saranno stati distrutti prima che qualsiasi ServletContextListeners venga informato della distruzione del contesto."

http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html

+0

Non dovrebbe quegli annulli essere in contextDestroyed()? – DaveH

+0

Sì ... immagino sia ora di andare a letto, grazie per avermelo fatto notare! –

+0

TimerTask # cancel non interromperà l'attività se è attualmente in esecuzione - si accerterà che non verrà mai più eseguita - ma a parte questo, direi che staresti bene con quello – DaveH

risposta

57

Do non utilizzare Timer in un ambiente Java EE! Se l'attività genera un'eccezione di runtime, l'intero Timer viene ucciso e non verrà più eseguito. Fondamentalmente è necessario riavviare l'intero server per farlo funzionare di nuovo. Inoltre, è sensibile ai cambiamenti nell'orologio di sistema.

Utilizzare invece ScheduledExecutorService. Non è sensibile alle eccezioni generate nelle attività né ai cambiamenti nell'orologio di sistema. È possibile arrestarlo tramite il suo metodo shutdownNow().

Ecco un esempio di come l'intero ServletContextListener implementazione può apparire come (nota: nessuna registrazione in web.xml richieste grazie al nuovo @WebListener annotazioni):

@WebListener 
public class BackgroundJobManager implements ServletContextListener { 

    private ScheduledExecutorService scheduler; 

    @Override 
    public void contextInitialized(ServletContextEvent event) { 
     scheduler = Executors.newSingleThreadScheduledExecutor(); 
     scheduler.scheduleAtFixedRate(new YourParsingJob(), 0, 5, TimeUnit.HOUR); 
    } 

    @Override 
    public void contextDestroyed(ServletContextEvent event) { 
     scheduler.shutdownNow(); 
    } 

} 
+2

Wow! Questa risposta è stata * incredibilmente * utile. Grazie mille. Per altri lettori, un punto di chiarimento ... Il 'new YourParsingJob()' dovrebbe essere un'istanza dell'interfaccia [Runnable] (http://docs.oracle.com/javase/7/docs/api/java/lang/Runnable .html) come spiega JavaDoc. –

+0

E cosa succede se ho bisogno di accedere ad alcune risorse come un 'EntityManager', puoi accedervi o devi usare' @ Singleton' con anntotazione '@ Startup'? – rekiem87

+2

@ rekiem87: se hai EJB a portata di mano, usa invece '@ Schedule'. Esempi: http://stackoverflow.com/a/8482933 e http://stackoverflow.com/q/7499769 (parte in basso). La domanda attuale riguarda Tomcat che non ha JPA ed EJB. Fai attenzione nel trovare le risposte ... – BalusC

1

Le servlet distruggono metodo viene chiamato come il servlet è in procinto di essere scaricato. È possibile annullare il timer dall'interno, a condizione di aver modificato l'ambito del parserTimer stesso per trasformarlo in una variabile di istanza. Non vedo un problema con questo a condizione che tu acceda solo da init e destroy.

Il servlet engine è libero di scaricare il servlet ogni volta che lo ritiene opportuno, ma in pratica lo vedo sempre e solo quando viene arrestato il motore del servlet - altre persone potrebbero avere altre esperienze di questo anche se ciò potrebbe dimostrarmi sbagliato .

-1

Provare a utilizzare il framework per la pianificazione.

Se si adatta Spring Framework, è possibile utilizzare le funzionalità di pianificazione incorporate.

Durante la pianificazione con Spring non ho mai avuto problemi a fermare il server delle applicazioni.

-1

Put parseTimer.purge() nel tuo onContetexyDestroyed.It rimuoverà tutti i timer nella coda se esistono.

Problemi correlati