/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.usergrid.persistence.core.executor;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;


/**
 * A task executor that allows you to submit tasks
 */
public class TaskExecutorFactory {

    public enum RejectionAction {
        ABORT,
        CALLERRUNS
    }
    /**
     * Create a task executor
     * @param schedulerName
     * @param maxThreadCount
     * @param maxQueueSize
     * @return
     */
    public static ThreadPoolExecutor createTaskExecutor( final String schedulerName, final int maxThreadCount,
                                                         final int maxQueueSize, RejectionAction rejectionAction ) {


        final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>( maxQueueSize );


        if(rejectionAction.equals(RejectionAction.ABORT)){

            return new MaxSizeThreadPool( queue, schedulerName, maxThreadCount );

        }
        else if(rejectionAction.equals(RejectionAction.CALLERRUNS)){

            return new MaxSizeThreadPoolCallerRuns( queue, schedulerName, maxThreadCount );

        }else{
            //default to the thread pool with ABORT policy
            return new MaxSizeThreadPool( queue, schedulerName, maxThreadCount );
        }

    }

    /**
     * Create a thread pool that will reject work if our audit tasks become overwhelmed
     */
    private static final class MaxSizeThreadPool extends ThreadPoolExecutor {

        public MaxSizeThreadPool( final BlockingQueue<Runnable> queue, final String poolName, final int maxPoolSize ) {
            super( maxPoolSize, maxPoolSize, 30, TimeUnit.SECONDS, queue, new CountingThreadFactory( poolName ) );
        }
    }

    /**
     * Create a thread pool that will implement CallerRunsPolicy if our tasks become overwhelmed
     */
    private static final class MaxSizeThreadPoolCallerRuns extends ThreadPoolExecutor {

        public MaxSizeThreadPoolCallerRuns( final BlockingQueue<Runnable> queue, final String poolName, final int maxPoolSize ) {
            super( maxPoolSize, maxPoolSize, 30, TimeUnit.SECONDS, queue,
                new CountingThreadFactory( poolName ), new ThreadPoolExecutor.CallerRunsPolicy() );
        }
    }


    /**
     * Thread factory that will name and count threads for easier debugging
     */
    private static final class CountingThreadFactory implements ThreadFactory {

        private final AtomicLong threadCounter = new AtomicLong();
        private final String poolName;


        private CountingThreadFactory( final String poolName ) {this.poolName = poolName;}


        @Override
        public Thread newThread( final Runnable r ) {
            final long newValue = threadCounter.incrementAndGet();

            final String threadName = poolName + "-" + newValue;

            Thread t = new Thread( r, threadName );

            //set it to be a daemon thread so it doesn't block shutdown
            t.setDaemon( true );

            return t;
        }
    }
}

@@ -872,182 +872,58 @@
 ort 
-java.util.concurrent.ArrayBlockingQueue;%0Aimport java.util.concurrent.BlockingQueue;%0Aimport java.util.concurrent.ThreadFactory;%0Aimport java.util.concurrent.ThreadPoolExecutor;
+org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0A
 %0Aimp
@@ -947,24 +947,17 @@
 current.
-TimeUnit
+*
 ;%0Aimport
@@ -1090,24 +1090,115 @@
 rFactory %7B%0A%0A
+    private static final Logger log = LoggerFactory.getLogger(TaskExecutorFactory.class);%0A%0A
     public e
@@ -3126,44 +3126,32 @@
 new 
-ThreadPoolExecutor.CallerRunsPolicy(
+RejectedHandler(poolName
 ) );
@@ -3902,38 +3902,740 @@
 mon(
- true );%0A%0A            return t
+true);%0A%0A            return t;%0A        %7D%0A    %7D%0A%0A    /**%0A     * The handler that will handle rejected executions and signal the interface%0A     */%0A    private static final class RejectedHandler implements RejectedExecutionHandler %7B%0A%0A        private final String poolName;%0A%0A        private RejectedHandler (final String poolName) %7Bthis.poolName = poolName;%7D%0A%0A        @Override%0A        public void rejectedExecution( final Runnable r, final ThreadPoolExecutor executor ) %7B%0A            log.warn( %22%7B%7D task queue full, rejecting task %7B%7D and running in thread %7B%7D%22, poolName, r, Thread.currentThread().getName() );%0A%0A            //We've decided we want to have a %22caller runs%22 policy, to just invoke the task when rejected%0A%0A            r.run()
 ;%0A  
@@ -4638,20 +4638,21 @@
 ;%0A        %7D%0A
+%0A
     %7D%0A%7D%0A

Add custom rejection handler that implements 'caller runs' which includes logging.