package ru.yandex.direct.api.v5.timeout;

import java.util.concurrent.locks.Condition;

import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;

import ru.yandex.direct.interruption.TimeoutInterrupter;

/**
 * Интерсептор предназначен для ограничения времени работы между
 * вызовом {@code handleRequest()} и любым из следующих вызовов:
 * {@code handleResponse()}, {@code handleFault()},
 * {@code afterCompletion()}.
 * <p>
 * Так как поток после прерывания предполагается использовать
 * дальше, в {@code afterCompletion()} сбрасывается
 * {@code interrupt}-статус потока. Если статус останется
 * выставлен, это может привести к генерации исключения,
 * например, в методах{@link Object#wait()},
 * {@link Condition#await()} и других, а так же нарушить работу
 * любого кода, явно проверяющего этот статус. В частности,
 * запись в {@link javax.servlet.ServletOutputStream}
 * будет провалена, что приведет к обрыву соединения.
 * <p>
 * Подробности работы прерывания потока смотреть здесь: {@link TimeoutInterrupter}.
 */
@Component
public class TimeoutInterceptor implements EndpointInterceptor {

    private final TimeoutInterrupter timeoutInterrupter;

    @Autowired
    public TimeoutInterceptor(@Value("${jetty.request_timeout}") int timeout) {
        timeoutInterrupter = new TimeoutInterrupter(timeout);
    }

    @Override
    public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
        timeoutInterrupter.start();
        return true;
    }

    @Override
    public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
        timeoutInterrupter.stop();
        return true;
    }

    @Override
    public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
        timeoutInterrupter.stop();
        return true;
    }

    @Override
    public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {
        timeoutInterrupter.stop();
        /*
            Сброс флага interrupted. Необходим для того, чтобы записать ответ в OutputStream
            в том случае, когда мы прерываем запрос по таймауту. С выставленным флагом interrupted
            отправка ответа невозможна.
         */
        Thread.interrupted();
    }

    @PreDestroy
    public void close() {
        timeoutInterrupter.close();
    }
}
