2012-02-07 21 views
20

Tengo un archivo WAR desplegado en el servidor Tomcat, se llamará a una de las clases en el momento del inicio, luego el método init() programará un temporizador para que se active cada 5 horas para realizar algunas tareas .Detener el temporizador programado al cerrar tomcat

Mi init() código es el siguiente:

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

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

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

Mi aplicación se ejecuta sin ningún problema, pero cuando el apagado del Tomcat mediante /etc/init.d/tomcat7 paro, entonces puedo comprobar el registro (catalina.out) tiene una entrada como esta:

SEVERE: Parece que la aplicación web [/ MyApplication] ha iniciado un hilo llamado [Timer-0] pero no ha podido detenerlo. Esto es muy probable que cree una pérdida de memoria.

entiendo que esto es causado por mí programar el temporizador, pero mi pregunta es:

  1. No me propuse setDeamon true, por lo que no debería impedir que el temporizador Tomcat desde el cierre, en lugar de dejado corriendo?
  2. ¿Puedo, en mi aplicación, detectar que Tomcat va a estar apagado y cancelar mi temporizador?
  3. ¿Cuáles son las otras soluciones que puedo usar para solucionar este problema?

¡Gracias!

ACTUALIZACIÓN

he cambiado de código a la siguiente basado en alguna búsqueda y la respuesta de 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) { 

} 

Ahora mi nueva pregunta:

  1. cancelo TimerTask primero y luego temporizador, es esto correcto?
  2. ¿Hay alguna otra cosa que deba hacer?

¡Gracias!

ACTUALIZACIÓN

No funciona. Puse una declaración de registro en el método contextDestroyed(), después de cerrar Tomcat, el archivo de registro solo tiene lo siguiente:

PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:45) :: DETECTAR servidor Tomcat VA A APAGAR PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:46) :: CANCELAR Task Timer y el temporizador

con cancelación COMPLETA es no ahí.

También verifiqué los procesos que se están ejecutando (no soy un experto en Linux, así que solo uso el Monitor de actividad de Mac).

  • Asegúrese de que hay un proceso de Java se ejecuta
  • iniciar Tomcat, tenga en cuenta el PID de ese proceso java
  • Parar Tomcat
  • Encontrado el proceso Tomcat se ha ido
  • iniciar Tomcat, tenga en cuenta el PID ese proceso java
  • instalar mi archivo de la guerra
  • Muestra el proceso, ver [Timer-0] hilo no es
  • Shutdown Tomcat
  • encontrado que el procedimiento está todavía allí
  • Muestra el proceso
  • Ver [Timer-0] es todavía allí

FIJO

he cambiado de código para parserTimer = new Timer(true); para que mi temporizador se ejecute como un hilo daemon porque se llama al contextDestroyed() después de que Tomcat se apaga.

"Todos los servlets y filtros se habrán destruido antes de que se notifique a ServletContextListeners sobre la destrucción del contexto".

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

+0

esos Cancela no deben estar en contextDestroyed()? – DaveH

+0

Sí ... creo que es hora de ir a la cama, ¡gracias por señalar! –

+0

TimerTask # cancel no interrumpirá la tarea si se está ejecutando actualmente; solo se asegurará de que nunca vuelva a ejecutarse, pero aparte de eso, yo diría que con eso estaría bien – DaveH

Respuesta

57

Do no usa Timer en un entorno Java EE! Si la tarea arroja una excepción de tiempo de ejecución, entonces se destruye el entero Timer y ya no se ejecutará. Básicamente, necesitas reiniciar todo el servidor para que vuelva a funcionar. Además, es sensible a los cambios en el reloj del sistema.

Use ScheduledExecutorService en su lugar. No es sensible a las excepciones lanzadas en las tareas ni a los cambios en el reloj del sistema. Puede cerrarlo por su método shutdownNow().

Aquí hay un ejemplo de cómo todo el ServletContextListener podemos hacer algo (nota: no hay registro en web.xml gracias necesarias a la nueva anotación @WebListener):

@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

¡Guau! Esta respuesta fue * asombrosamente * útil. Muchas gracias. Para otros lectores, un punto de aclaración ... El 'nuevo YourParsingJob()' debe ser una instancia de la interfaz [Runnable] (http://docs.oracle.com/javase/7/docs/api/java/lang/Runnable .html) como explica JavaDoc. –

+0

Y qué pasa si necesito acceder a algún recurso como un 'EntityManager', ¿puedes tener acceso a esto o tienes que usar' @ Singleton' con una anotación '@ Startup'? – rekiem87

+2

@ rekiem87: si tiene EJB a mano, use '@ Schedule' en su lugar. Ejemplos: http://stackoverflow.com/a/8482933 y http://stackoverflow.com/q/7499769 (parte inferior). La pregunta actual es sobre Tomcat que no tiene JPA y EJB. Tenga cuidado al buscar respuestas ... – BalusC

1

Los servlets destruyen método se denomina como el servlet está a punto de ser descargados. Podría cancelar el temporizador desde allí, siempre que haya modificado el alcance del temporizador del analizador para que sea una variable de instancia. No veo ningún problema con eso, siempre que accedas solo desde init y destruyas.

El servlet engine puede descargar el servlet cuando lo considere oportuno, pero en la práctica solo lo he visto llamar cuando se para el motor de servlets; otras personas pueden tener otras experiencias de esto, aunque eso podría ser una equivocación .

-1

Intente utilizar el marco para la programación.

Si adapta Spring Framework puede usar la capacidad de programación incorporada.

Al programar con Spring nunca tuve problemas para detener el servidor de aplicaciones.

-1

Ponga parseTimer.purge() en su onContetexyDestroyed.Eliminará todos los temporizadores en la cola, si es que existen.

Cuestiones relacionadas