package online.filter.develop;

import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * HotDeploy用ユーティリティ
 *
 * @author Tadashi Nakayama
 */
public interface S2HotDeploy {

    /**
     * チェイン処理
     *
     * @param request ServletRequest
     * @param response ServletResponse
     * @param chain FilterChain
     * @param consumer FilterConsumer
     * @throws IOException IOException
     * @throws ServletException ServletException
     */
    default void doChain(final ServletRequest request, final ServletResponse response,
            final FilterChain chain, final FilterConsumer<ClassLoader> consumer)
            throws IOException, ServletException {
        final String clazz = S2HotDeploy.class.getName();
        final ClassLoader org = Thread.currentThread().getContextClassLoader();
        try {
            if (request.getAttribute(clazz) == null) {
                final ClassLoader cl = AccessController.doPrivileged(getClassLoaderSupplier(org));
                try {
                    request.setAttribute(clazz, cl);

                    consumer.accept(cl);

                } finally {
                    request.removeAttribute(clazz);
                }
            } else {
                final ClassLoader cl = ClassLoader.class.cast(request.getAttribute(clazz));
                Thread.currentThread().setContextClassLoader(cl);
                chain.doFilter(request, response);
            }
        } finally {
            Thread.currentThread().setContextClassLoader(org);
        }
    }

    /**
     * クラスローダーSupplier取得
     *
     * @param org 元クラスローダー
     * @return クラスローダーSupplier
     */
    PrivilegedAction<ClassLoader> getClassLoaderSupplier(ClassLoader org);

    /**
     * フィルター用
     *
     * @author Tadashi Nakayama
     * @param <T> ジェネリックス
     */
    @FunctionalInterface
    interface FilterConsumer<T> {
        /**
         * accept
         *
         * @param t T
         * @throws IOException IOException
         * @throws ServletException ServletException
         */
        void accept(T t) throws IOException, ServletException;
    }
}
