La función destroy() del servlet se ejecuta cuando éste va a ser destruido (normalmente por la detención del servidor). No se invoca cuando se termina la petición, sino cuando se va a descargar el servlet de memoria.
Cuando se destruye el servlet las peticiones que están pendientes de respuesta (no han entrado en doGet, doPost o service) no serán atendidas. El problema no es este, sino la posibilidad de que en destroy() liberemos recursos que son necesarios a peticiones que están en tramite de respuesta (peticiones que están dentro de doGet, doPost o service). Esto puede producir excepciones, inconsistencias, etc.
El programador debe liberar o cerrar recursos, como conexiones o flujos de entrada/salida. Ante esto, surge un interrogante, en peticiones de larga resolución pudiera ocurrir que destroy() libere recursos que todavía son necesarios (todavía se está procesando la funcción doGet/doPost/service). Para evitar esto el servidor tiene dos estrategias: no ejecuta destroy() hasta que han terminado las llamadas a las funciones de respuesta (doGet, doPost y service) o, en segundo lugar, hasta que termine un "periodo de gracia". Cada servidor implementa sus estrategias.
Para más información: Tutorial de Sun
Una estrategia para terminar de forma segura
En este capítulo vamos a ver una estrategia para liberar recursos de forma segura, algo relevante sobre todo cuando estamos procesando peticiones de largo periodo de respuesta. Esta estrategia se compone de diversas acciones:
private int cont_peticiones = 0; // Contador de peticiones
/*** Métodos para mantener un contador de peticiones o servicios ***/
protected synchronized void inc_cont_peticion() { cont_peticiones++; }
protected synchronized void dec_cont_peticion() { cont_peticiones--; }
protected synchronized int obt_cont_peticion() { return cont_peticiones; }
La expresión synchronized se explica en el capítulo dedicado a programación multihilos. Aquí nos quedamos
con la idea de que esta expresión nos asegura que un hilo o petición no entrará en inc_cont_peticion()
(incrementa el contador) hasta que otra no termine dec_cont_peticion() (decrementa el contador).
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
inc_cont_peticion(); // Incrementar contador de peticiones
.... definir la respuesta ....
dec_cont_peticion(); // Decrementar contador de peticiones
}
public void destroy() {
while (obt_cont_peticion() > 0) {
try { Thread.sleep(500); }
catch (InterruptedException e) { e.printStackTrace(); }
}
... liberar recursos ....
}
A continuación puede ver un ejemplo de como cerrar una conexión a base de datos. Lo primero que hacemos es asegurarnos que la conexión no sea null (otro proceso o hilo puede haberla liberado). La llamada a close() requiere gestionar la excepción SQLException:
void cerrar_conexion() {
try {
if ( con != null )
if (!con.isClosed())
conexion.close();
}
catch (SQLException e) { e.printStackTrace(); }
}