org.apache.ranger.common.TestTimedExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ranger.common.TestTimedExecutor.java

Source

/*
 * 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.ranger.common;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;

public class TestTimedExecutor {

    private static final Log LOG = LogFactory.getLog(TestTimedExecutor.class);

    @Before
    public void before() {

    }

    @Test
    public void test() throws InterruptedException {
        /*
         * Create a pool with 2 threads and queue size of 3 such that 6th item should get rejected right away due to capacity.
         */
        int poolSize = 2;
        int queueSize = 3;
        _configurator = new TimedExecutorConfigurator(poolSize, queueSize);
        // Just toa void thread shutting down and restarting set keep alive to high value.
        _executor.initialize(_configurator);

        // now create 2 callalbles that would keep waiting unless we ask them to proceed
        // create an executor which would simulate simultaneous threads calling into executor to perform lookups
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        /*
         * We would have 2 permits for 10 callables, such that
         * - 2 should succeed
         * - 5 should timeout (2 in pool + 3 in queue)
         * - 3 should get rejected.
         */
        Semaphore semaphore = new Semaphore(2);
        /*
         * We need a latch to keep track of when the processing is done so we can check the results of teh test
         */
        CountDownLatch latch = new CountDownLatch(10);
        // Callables will record exception in this map
        final ConcurrentMap<String, AtomicInteger> results = new ConcurrentHashMap<String, AtomicInteger>();
        for (int i = 0; i < 10; i++) {
            LookupTask lookupTask = new LookupTask(i, semaphore);
            TimedTask timedTask = new TimedTask(_executor, lookupTask, 1, TimeUnit.SECONDS, results, latch);
            Future<Integer> aFuture = executorService.submit(timedTask);
            futures.add(aFuture);
        }
        // Let's wait for the threads to finish
        LOG.debug("Starting to wait for threadpool to finish");
        latch.await();
        /*
         * depending on how threads get scheduled the count in results would vary, except we know for sure that.
         * - 2 must succeed since we have exactly 2 permits available.
         * - sum of timed out and rejected must be equal to 8.
           * - at least 3 and no more than 5 tasks must get rejected.
           * - at least 3 and no more than 5 tasks must get timed out
         */
        int successCount = results.get("success").get();
        int timeoutCount = results.get("java.util.concurrent.TimeoutException").get();
        int rejectedCount = results.get("java.util.concurrent.RejectedExecutionException").get();
        assertEquals("success count", 2, successCount);
        assertTrue("timeout[" + timeoutCount + "]: 3 <= count(timeout) <= 5",
                timeoutCount >= 3 && timeoutCount <= 5);
        assertTrue("rejected[" + rejectedCount + "]: 3 <= count(timeout) <= 5",
                rejectedCount >= 3 && rejectedCount <= 5);
        assertEquals("total should equal 10", 10, successCount + timeoutCount + rejectedCount);
        _executor.shutdown();
    }

    static final String format = "%15s id: %2d";

    static class LookupTask implements Callable<Integer> {

        final int _id;
        final private Semaphore _semaphore;

        public LookupTask(int id, Semaphore latch) {
            _id = id;
            _semaphore = latch;
        }

        int getId() {
            return _id;
        }

        @Override
        public Integer call() throws Exception {
            LOG.debug(String.format(format, "Starting", _id));
            _semaphore.acquire();
            LOG.debug(String.format(format, "Acquired", _id));
            LOG.debug(String.format(format, "Ended", _id));
            return _id;
        }

    }

    static class TimedTask implements Callable<Integer> {

        final LookupTask _callable;
        final TimedExecutor _executor;
        final ConcurrentMap<String, AtomicInteger> _results;
        final long _timeout;
        final TimeUnit _unit;
        final CountDownLatch _latch;

        public TimedTask(TimedExecutor executor, LookupTask callable, int timout, TimeUnit unit,
                ConcurrentMap<String, AtomicInteger> results, CountDownLatch latch) {
            _callable = callable;
            _executor = executor;
            _results = results;
            _timeout = timout;
            _unit = unit;
            _latch = latch;
        }

        @Override
        public Integer call() throws Exception {
            int id = _callable.getId();
            LOG.debug(String.format(format, "Submitting", id));
            try {
                Integer result = _executor.timedTask(_callable, _timeout, _unit);
                LOG.debug(String.format(format, "Finished", id));
                recordResult(_results, "success");
                return result;
            } catch (Exception e) {
                LOG.debug(String.format(format, "Exception", id));
                recordResult(_results, e);
                // re-throw caught exception
                throw e;
            } finally {
                _latch.countDown();
            }
        }

    }

    static void recordResult(ConcurrentMap<String, AtomicInteger> results, String key) {
        if (results.containsKey(key)) {
            results.get(key).incrementAndGet();
        } else {
            AtomicInteger previous = results.putIfAbsent(key, new AtomicInteger(1));
            if (previous != null) { // a value was already associated with the key
                previous.incrementAndGet();
            }
        }
    }

    static void recordResult(ConcurrentMap<String, AtomicInteger> results, Exception e) {
        String exceptionName = e.getClass().getCanonicalName();
        recordResult(results, exceptionName);
    }

    private TimedExecutorConfigurator _configurator;
    private TimedExecutor _executor = new TimedExecutor();
}