<commit_before>/*
 * Copyright (C) 2015 Signicat AS
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.signicat.hystrix.servlet;

import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.contrib.servopublisher.HystrixServoMetricsPublisher;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.hystrix.strategy.HystrixPlugins;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import rx.Observable;
import rx.Observer;

/**
 * Wrapper class for executing any {@link javax.servlet.http.HttpServlet} within a Hystrix thread pool. Subclass this,
 * create a no-arg constructor in the subclass, and pass the HttpServlet to wrap to the superclass. Then configure the
 * new subclass to run in web.xml, instead of the old (wrapped) servlet. If you want to control which thread pool a
 * request ends up executing within, make the wrapped servlet implement
 * {@link com.signicat.hystrix.servlet.HystrixAwareServlet#getCommandGroupKey(javax.servlet.http.HttpServletRequest)}.
 *
 * @author <NAME> &lt;<EMAIL>&gt;
 */
@WebServlet(asyncSupported = true)
public class AsyncWrapperServlet extends HttpServlet {

    public static final String DEFAULT_COMMAND_GROUP_KEY = "default";
    private static final Logger log = LoggerFactory.getLogger(AsyncWrapperServlet.class);
    public static final long DEFAULT_TIMEOUT = 50000L;
    private static final int HYSTRIX_ADDED_TIMEOUT_DELAY = 10000; //servlet cntnr will time things out bf Hystrix does
    public static final int DEFAULT_CORE_POOL_SIZE = 100;
    private final HttpServlet wrappedServlet;
    private final long timeoutMillis;
    private final int corePoolSize;

    static {
        try {
            HystrixPlugins.getInstance().registerMetricsPublisher(HystrixServoMetricsPublisher.getInstance());
        } catch (IllegalStateException ignored) {
        }
        try {
            HystrixPlugins.getInstance().registerConcurrencyStrategy(ConcurrencyStrategyWithCoreTimeOut.getInstance());
        } catch (IllegalStateException ignored) {
        }
    }

    public AsyncWrapperServlet(final HttpServlet wrappedServlet) {
        this(wrappedServlet, DEFAULT_TIMEOUT, DEFAULT_CORE_POOL_SIZE);
    }

    public AsyncWrapperServlet(final HttpServlet wrappedServlet, final long timeoutMillis, final int corePoolSize) {
        this.wrappedServlet = wrappedServlet;
        this.timeoutMillis = timeoutMillis;
        this.corePoolSize = corePoolSize;
    }

    @Override
    public void init() throws ServletException {
        super.init();
        wrappedServlet.init();
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        wrappedServlet.init(config);
    }

    @Override
    public void destroy() {
        super.destroy();
        Hystrix.reset(5, TimeUnit.SECONDS);
        wrappedServlet.destroy();
    }

    @Override
    public void service(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        AsyncContext asyncContext = req.startAsync();
        asyncContext.setTimeout(timeoutMillis);
        TimeoutAwareHttpServletRequest timeoutAwareHttpServletReq = new TimeoutAwareHttpServletRequest(req);
        TimeoutAwareHttpServletResponse timeoutAwareHttpServletResp = new TimeoutAwareHttpServletResponse(resp);
        asyncContext.addListener(new BaseServletAsyncListener(timeoutAwareHttpServletReq,
                                                              timeoutAwareHttpServletResp));

        Runnable runBefore = onBeforeCommandSubmit();
        Runnable runAfter = onAfterCommandExecute();

        final String key = getCommandGroupKey(wrappedServlet, req);
        final String path = req.getContextPath() + req.getServletPath() +
                            (req.getPathInfo() == null ? "" : req.getPathInfo());
        log.info("Scheduling Hystrix command with key '" + key + "' for path: '" + path + "'.");

        //double thread pool size for pool with magic name 'default'
        final int maximumSize = DEFAULT_COMMAND_GROUP_KEY.equals(key) ? (corePoolSize * 2) : corePoolSize;
        final int coreSize    = Math.min(10, maximumSize);
        final int queueSize          = (int) (((double) corePoolSize) * 1.4d);
        final int queueRejectionSize = (int) (((double) corePoolSize) * 1.2d);

        BaseServletCommand command =
                new BaseServletCommand(
                        HystrixCommand.Setter
                                .withGroupKey(
                                        HystrixCommandGroupKey.Factory.asKey(key))
                                .andCommandKey(HystrixCommandKey.Factory.asKey(key))
                                .andCommandPropertiesDefaults(
                                        HystrixCommandProperties.Setter()
                                                .withExecutionTimeoutInMilliseconds(
                                                        (int) timeoutMillis + HYSTRIX_ADDED_TIMEOUT_DELAY)
                                                .withCircuitBreakerEnabled(false)
                                                .withExecutionIsolationStrategy(
                                                        HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                                                .withFallbackEnabled(false)
                                                .withRequestLogEnabled(false))
                                .andThreadPoolPropertiesDefaults(
                                        HystrixThreadPoolProperties.Setter()
                                                .withAllowMaximumSizeToDivergeFromCoreSize(true)
                                                .withCoreSize(coreSize)
                                                .withMaximumSize(maximumSize)
                                                .withMaxQueueSize(queueSize)
                                                .withKeepAliveTimeMinutes(1)
                                                .withQueueSizeRejectionThreshold(queueRejectionSize)),
                        timeoutAwareHttpServletReq,
                        timeoutAwareHttpServletResp,
                        runBefore,
                        runAfter);

        Observable<Object> observable = command.observe();
        observable.subscribe(new BaseServletObserver(asyncContext));

        onAfterCommandSubmit();
    }

    protected String getCommandGroupKey(HttpServlet srv, HttpServletRequest req) {<commit_msg>remove diverging core/max size after some testing, it became apparent that the diverging core/max size functionality never worked as intended in older versions of hystrix-servlet, as it was always overidden by the getExecutor method in HystrixThreadPoolDefault. this revision is closer to the original behaviour, except when prestarting core threads<commit_after>/*
 * Copyright (C) 2015 Signicat AS
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.signicat.hystrix.servlet;

import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.contrib.servopublisher.HystrixServoMetricsPublisher;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.hystrix.strategy.HystrixPlugins;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import rx.Observable;
import rx.Observer;

/**
 * Wrapper class for executing any {@link javax.servlet.http.HttpServlet} within a Hystrix thread pool. Subclass this,
 * create a no-arg constructor in the subclass, and pass the HttpServlet to wrap to the superclass. Then configure the
 * new subclass to run in web.xml, instead of the old (wrapped) servlet. If you want to control which thread pool a
 * request ends up executing within, make the wrapped servlet implement
 * {@link com.signicat.hystrix.servlet.HystrixAwareServlet#getCommandGroupKey(javax.servlet.http.HttpServletRequest)}.
 *
 * @author <NAME> &lt;<EMAIL>&gt;
 */
@WebServlet(asyncSupported = true)
public class AsyncWrapperServlet extends HttpServlet {

    public static final String DEFAULT_COMMAND_GROUP_KEY = "default";
    private static final Logger log = LoggerFactory.getLogger(AsyncWrapperServlet.class);
    public static final long DEFAULT_TIMEOUT = 50000L;
    private static final int HYSTRIX_ADDED_TIMEOUT_DELAY = 10000; //servlet cntnr will time things out bf Hystrix does
    public static final int DEFAULT_CORE_POOL_SIZE = 100;
    private final HttpServlet wrappedServlet;
    private final long timeoutMillis;
    private final int corePoolSize;

    static {
        try {
            HystrixPlugins.getInstance().registerMetricsPublisher(HystrixServoMetricsPublisher.getInstance());
        } catch (IllegalStateException ignored) {
        }
        try {
            HystrixPlugins.getInstance().registerConcurrencyStrategy(ConcurrencyStrategyWithCoreTimeOut.getInstance());
        } catch (IllegalStateException ignored) {
        }
    }

    public AsyncWrapperServlet(final HttpServlet wrappedServlet) {
        this(wrappedServlet, DEFAULT_TIMEOUT, DEFAULT_CORE_POOL_SIZE);
    }

    public AsyncWrapperServlet(final HttpServlet wrappedServlet, final long timeoutMillis, final int corePoolSize) {
        this.wrappedServlet = wrappedServlet;
        this.timeoutMillis = timeoutMillis;
        this.corePoolSize = corePoolSize;
    }

    @Override
    public void init() throws ServletException {
        super.init();
        wrappedServlet.init();
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        wrappedServlet.init(config);
    }

    @Override
    public void destroy() {
        super.destroy();
        Hystrix.reset(5, TimeUnit.SECONDS);
        wrappedServlet.destroy();
    }

    @Override
    public void service(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        AsyncContext asyncContext = req.startAsync();
        asyncContext.setTimeout(timeoutMillis);
        TimeoutAwareHttpServletRequest timeoutAwareHttpServletReq = new TimeoutAwareHttpServletRequest(req);
        TimeoutAwareHttpServletResponse timeoutAwareHttpServletResp = new TimeoutAwareHttpServletResponse(resp);
        asyncContext.addListener(new BaseServletAsyncListener(timeoutAwareHttpServletReq,
                                                              timeoutAwareHttpServletResp));

        Runnable runBefore = onBeforeCommandSubmit();
        Runnable runAfter = onAfterCommandExecute();

        final String key = getCommandGroupKey(wrappedServlet, req);
        final String path = req.getContextPath() + req.getServletPath() +
                            (req.getPathInfo() == null ? "" : req.getPathInfo());
        log.info("Scheduling Hystrix command with key '" + key + "' for path: '" + path + "'.");

        //double thread pool size for pool with magic name 'default'
        final int coreSize = DEFAULT_COMMAND_GROUP_KEY.equals(key) ? (corePoolSize * 2) : corePoolSize;
        final int queueSize          = (int) (((double) corePoolSize) * 1.4d);
        final int queueRejectionSize = (int) (((double) corePoolSize) * 1.2d);

        BaseServletCommand command =
                new BaseServletCommand(
                        HystrixCommand.Setter
                                .withGroupKey(
                                        HystrixCommandGroupKey.Factory.asKey(key))
                                .andCommandKey(HystrixCommandKey.Factory.asKey(key))
                                .andCommandPropertiesDefaults(
                                        HystrixCommandProperties.Setter()
                                                .withExecutionTimeoutInMilliseconds(
                                                        (int) timeoutMillis + HYSTRIX_ADDED_TIMEOUT_DELAY)
                                                .withCircuitBreakerEnabled(false)
                                                .withExecutionIsolationStrategy(
                                                        HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                                                .withFallbackEnabled(false)
                                                .withRequestLogEnabled(false))
                                .andThreadPoolPropertiesDefaults(
                                        HystrixThreadPoolProperties.Setter()
                                                .withCoreSize(coreSize)
                                                .withMaxQueueSize(queueSize)
                                                .withKeepAliveTimeMinutes(1)
                                                .withQueueSizeRejectionThreshold(queueRejectionSize)),
                        timeoutAwareHttpServletReq,
                        timeoutAwareHttpServletResp,
                        runBefore,
                        runAfter);

        Observable<Object> observable = command.observe();
        observable.subscribe(new BaseServletObserver(asyncContext));

        onAfterCommandSubmit();
    }

    protected String getCommandGroupKey(HttpServlet srv, HttpServletRequest req) {
{'fraction_non_alphanumeric': 0.07030148391861948, 'fraction_numerical': 0.004163029611226751, 'mean_word_length': 2.368921058584031, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 58, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7590429', 'n_tokens_mistral': 3610, 'n_tokens_neox': 3526, 'n_words': 861}