Веб-контейнер может определить, что сервлет должен быть удалён (например, когда контейнер хочет освободить память или когда он выключен). В таком случае контейнер вызывает метод destroy
интерфейса Servlet
. В этом методе вы освобождаете любые ресурсы, которые использует сервлет, и сохраняете любое персистентное состояние. Метод destroy
освобождает объект базы данных, созданный в методе init
.
Все сервисные методы сервлета должны быть завершены при удалении сервлета. Сервер пытается убедиться в этом, вызывая метод destroy
только после того, как все запросы на обслуживание выполнены или по истечении заданного времени (grace period), в зависимости от того, что наступит раньше. Если в вашем сервлете есть операции, которые могут выполняться дольше, чем специальный период сервера (grace period), эти операции могут выполняться при вызове destroy
. Вы должны убедиться, что все потоки, по-прежнему обрабатывающие клиентские запросы, завершены.
В оставшейся части этого раздела объясняется, как это сделать.
-
Отслеживайте, сколько потоков в настоящее время выполняет метод service
.
-
Обеспечьте чистое завершение работы с помощью метода destroy
, чтобы уведомить долго выполняющиеся потоки о завершении работы и дождаться их завершения.
-
Периодически опрашивайте долго выполняющиеся методы для проверки завершения их работы и, при необходимости, их прекращение, очистку и возврат результатов.
Сервис отслеживания запросов
Для отслеживания запросов на обслуживание:
-
Включите в класс сервлета поле, которое подсчитывает количество запущенных сервисных методов.
Поле должно иметь синхронизированные методы доступа для увеличения, уменьшения и возврата значения:
public class ShutdownExample extends HttpServlet {
private int serviceCounter = 0;
...
// Методы доступа serviceCounter
protected synchronized void enteringServiceMethod() {
serviceCounter++;
}
protected synchronized void leavingServiceMethod() {
serviceCounter--;
}
protected synchronized int numServices() {
return serviceCounter;
}
}
Метод service
должен увеличивать сервисный счётчик каждый раз, когда выполняется вход в метод, и должен уменьшать счётчик каждый раз, когда выполняется выход из метода. Это один из немногих случаев, когда ваш дочерний класс HttpServlet
должен переопределять метод service
. Новый метод должен вызывать super.service
для сохранения функциональности исходного метода service
:
protected void service(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException,IOException {
enteringServiceMethod();
try {
super.service(req, resp);
} finally {
leavingServiceMethod();
}
}
Уведомление методов о выключении
Чтобы обеспечить полное выключение, ваш метод destroy
не должен освобождать какие-либо общие ресурсы, пока не будут завершены все запросы на обслуживание:
-
Проверьте сервисный счётчик.
-
Уведомите долго выполняющиеся методы о том, что пора завершать работу.
Для этого уведомления требуется другое поле. Поле должно иметь обычные методы доступа:
public class ShutdownExample extends HttpServlet {
private boolean shuttingDown;
...
// Методы доступа shuttingDown
protected synchronized void setShuttingDown(boolean flag) {
shuttingDown = flag;
}
protected synchronized boolean isShuttingDown() {
return shuttingDown;
}
}
Вот пример метода destroy
, использующего эти поля для обеспечения корректного завершения работы:
public void destroy() {
/* Проверка, есть ли ещё работающие сервисные методы, /*
/* и если они есть, их остановка. */
if (numServices()> 0) {
setShuttingDown(true);
}
/* Ожидание остановки сервисного метода. */
while (numServices()> 0) {
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
}
Корректное создание долго выполняющихся методов
Последний шаг в обеспечении чистого завершения работы — заставить все долго выполняющиеся методы вести себя корректно. Долго выполняющиеся методы должны проверять значение поля, которое уведомляет их о выключении, и они должны прервать свою работу, если необходимо:
public void doPost(...) {
...
for(i = 0; ((i < lotsOfStuffToDo) &&
!isShuttingDown()); i++) {
try {
partOfLongRunningOperation(i);
} catch (InterruptedException e) {
...
}
}
}