Manejar Threads de Servicio a la Terminación de un Servlet

Todos los métodos de servicio de un servlet deberían estar terminados cuando se elimina el servlet. El servidor intenta asegurarse llamando al método destroy sólo después de que todas las peticiones de servicio hayan retornado, o después del periodo de tiempo de gracia específico del servido, lo que ocurra primero. Si nuestro servlet tiene operaciones que tardan mucho tiempo en ejecutarse (esto es, operaciones que tardan más que el tiempo concedido por el servidor), estas operaciones podrían estar ejecutándose cuando se llame al método destroy. Debemos asegurarnos de que cualquier thread que maneje peticiones de cliente se hayan completado; el resto de está página describe una técnica para hacer esto

Si nuestro servlet tiene peticiones de servicio potencialmente largas, debemos utilizar las técnicas de esta lección para:

Peticiones de Seguimiento de Servicio

Para seguir la pista a una petición de servicio, incluimos un campo en nuestra clase servlet que cuente el número de métodos de servicio que se están ejecutando. El campo deberá tener acceso a métodos para incrementar, decrementar y devolver su valor. Por ejemplo:
public ShutdownExample extends HttpServlet {
    private int serviceCounter = 0;
    ...
    //Access methods for serviceCounter
    protected synchronized void enteringServiceMethod() {
	serviceCounter++;
    }
    protected synchronized void leavingServiceMethod() {
        serviceCounter--;
    }
    protected synchronized int numServices() {
	return serviceCounter;
    }
}
El método service debería incrementar el contador de servicios cada vez que se entre en él y decrementarlo cada vez que se salga de él. Esta es una de las pocas veces que al subclasificar la clase HttpServlet debamos sobreescribir el método service. El nuevo método debería llamar al super.service para preservar la funcionalidad del método HttpServlet.service original.
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
	enteringServiceMethod();
	try {
            super.service(req, resp);
        } finally {
            leavingServiceMethod();
        }
    }

Proporcionar Limpieza a la Desconexión

Para proporcionar esta limpieza, nuestro método destroy no debería destruir ningún recurso compartido hasta que todas las peticiones de servicio se hayan completado. Una parte de esto es chequear el contador de servicos. Otra parte es notificar a los métodos de larga duración que es la hora de la desconexión. Para esto, se necesita otro campo con sus métodos de acceso normales. Por ejemplo:
public ShutdownExample extends HttpServlet {
    private Boolean shuttingDown;
    ...
    //Access methods for shuttingDown
    protected setShuttingDown(Boolean flag) {
	shuttingDown = flag;
    }
    protected Boolean isShuttingDown() {
	return shuttingDown;
    }
}
Abajo podemos ver un método destroy que utiliza estos campos para proporcionar una limpieza de desconexión:
    public void destroy() {

        /* Check to see whether there are still service methods running,
	 * and if there are, tell them to stop. */
	if (numServices() > 0) {
	    setShuttingDown(true);
        }

	/* Wait for the service methods to stop.  */
	while(numServices() > 0) {
            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
            }
        }
    }

Crear Métodos de Larga Duración Educados

El paso final para proporcionar una limpieza de desconexión es crear métodos de larga duración que sean educados. Estos métodos deberían comprobar el valor del campo que notifica las desconexiones, e interrumpir su trabajo si es necesario. Por ejemplo:
    public void doPost(...) {
        ...
	for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++) {
	    try {
		partOfLongRunningOperation(i);
	    } catch (InterruptedException e) {
            }
        }
    }

Ozito