/**
 * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipse.hawkbit.autoconfigure.scheduling;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * Central event processors inside update server.
 *
 */
@Configuration
@EnableConfigurationProperties(AsyncConfigurerThreadpoolProperties.class)
public class ExecutorAutoConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorAutoConfiguration.class);

    @Autowired
    private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;

    /**
     * @return ExecutorService with security context availability in thread
     *         execution..
     */
    @Bean
    @ConditionalOnMissingBean
    public Executor asyncExecutor() {
        return new DelegatingSecurityContextExecutor(threadPoolExecutor());
    }

    /**
     * @return central ThreadPoolExecutor for general purpose multi threaded
     *         operations. Tries an orderly shutdown when destroyed.
     */
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolExecutor threadPoolExecutor() {
        final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(
                asyncConfigurerProperties.getQueuesize());
        final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(),
                asyncConfigurerProperties.getMaxthreads(), asyncConfigurerProperties.getIdletimeout(),
                TimeUnit.MILLISECONDS, blockingQueue,
                new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
        threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> LOGGER.warn(
                "Reject runnable for centralExecutorService, reached limit of queue size {}",
                executor.getQueue().size()));

        return threadPoolExecutor;
    }

    /**
     * @return the executor for UI background processes.
     */
    @Bean(name = "uiExecutor")
    @ConditionalOnMissingBean(name = "uiExecutor")
    public Executor uiExecutor() {
        final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(20);
        final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 20, 10000, TimeUnit.MILLISECONDS,
                blockingQueue, new ThreadFactoryBuilder().setNameFormat("ui-executor-pool-%d").build());
        threadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return new DelegatingSecurityContextExecutor(threadPoolExecutor);
    }

    /**
     * @return {@link TaskExecutor} for task execution
     */
    @Bean
    @ConditionalOnMissingBean
    public TaskExecutor taskExecutor() {
        return new ConcurrentTaskExecutor(asyncExecutor());
    }

    /**
     * @return {@link ScheduledExecutorService} based on
     *         {@link #threadPoolTaskScheduler()}.
     */
    @Bean
    @ConditionalOnMissingBean
    public ScheduledExecutorService scheduledExecutorService() {
        return threadPoolTaskScheduler().getScheduledExecutor();
    }

    /**
     * @return {@link ThreadPoolTaskScheduler} for scheduled operations.
     */
    @Bean
    @ConditionalOnMissingBean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

}

@@ -594,32 +594,97 @@
 adPoolExecutor;%0A
+import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;%0A
 import java.util
@@ -2869,35 +2869,82 @@
 %25d%22).build()
+,%0A                new PoolSizeExceededPolicy()
 );%0A
+%0A
         
 threadPoolEx
@@ -2923,32 +2923,39 @@
 cy());%0A%0A        
+return 
 threadPoolExecut
@@ -2960,103 +2960,288 @@
 utor
-.setRejectedExecutionHandler((r, executor) -%3E LOGGER.warn(%0A                %22Reject runnable for
+;%0A    %7D%0A%0A    private static class PoolSizeExceededPolicy extends CallerRunsPolicy %7B%0A        @Override%0A        public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) %7B%0A            LOGGER.warn(%0A                    %22Caller has to run on its own instead of
  cen
@@ -3306,24 +3306,28 @@
             
+    
 executor.get
@@ -3341,19 +3341,17 @@
 .size())
-);%0A
+;
 %0A       
@@ -3355,34 +3355,59 @@
     
-return threadPoolExecutor;
+    super.rejectedExecution(r, executor);%0A        %7D
 %0A   

Set new policy for central executor pool to avoid loosing data.