/*
* Copyright Terracotta, Inc.
*
* Licensed 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.ehcache.impl.internal.executor;
import org.ehcache.impl.config.executor.PooledExecutionServiceConfiguration;
import org.ehcache.impl.internal.util.ThreadFactoryUtil;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ludovic Orban
*/
public class PooledExecutionServiceTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
PooledExecutionService pooledExecutionService;
@After
public void after() {
if(pooledExecutionService != null) {
pooledExecutionService.stop();
}
}
@Test
public void testEmptyConfigThrowsAtStart() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
pooledExecutionService = new PooledExecutionService(configuration);
expectedException.expectMessage("Pool configuration is empty");
pooledExecutionService.start(null);
}
@Test
public void testGetOrderedExecutorFailsOnNonExistentPool() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
configuration.addPool("aaa", 0, 1);
pooledExecutionService = new PooledExecutionService(configuration);
pooledExecutionService.start(null);
expectedException.expectMessage("Pool 'abc' is not in the set of available pools [aaa]");
pooledExecutionService.getOrderedExecutor("abc", new LinkedBlockingDeque<Runnable>());
}
@Test
public void testGetOrderedExecutorFailsOnNonExistentDefaultPool() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
configuration.addPool("aaa", 0, 1);
pooledExecutionService = new PooledExecutionService(configuration);
pooledExecutionService.start(null);
expectedException.expectMessage("Null pool alias provided and no default pool configured");
pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<Runnable>());
}
@Test
public void testGetOrderedExecutorSucceedsOnExistingPool() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
configuration.addPool("aaa", 0, 1);
pooledExecutionService = new PooledExecutionService(configuration);
pooledExecutionService.start(null);
ExecutorService aaa = pooledExecutionService.getOrderedExecutor("aaa", new LinkedBlockingDeque<Runnable>());
aaa.shutdown();
}
@Test
public void testGetOrderedExecutorSucceedsOnExistingDefaultPool() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
configuration.addDefaultPool("dflt", 0, 1);
pooledExecutionService = new PooledExecutionService(configuration);
pooledExecutionService.start(null);
ExecutorService dflt = pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<Runnable>());
dflt.shutdown();
}
@Test
public void testAllThreadsAreStopped() throws Exception {
PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration();
configuration.addDefaultPool("dflt", 0, 1);
pooledExecutionService = new PooledExecutionService(configuration);
pooledExecutionService.start(null);
final CountDownLatch latch = new CountDownLatch(1);
pooledExecutionService.getScheduledExecutor("dflt")
.execute(new Runnable() {
@Override
public void run() {
latch.countDown();
}});
latch.await(30, TimeUnit.SECONDS);
pooledExecutionService.stop();
// Note: This test also tends to fail when other tests are not closing stores or cache managers correctly. So it will
// also print these threads below and fail. To go look after them, turn ThreadFactoryUtil.DEBUG to true to get a full
// stacktrace to the creation point.
detectLeakingThreads();
}
public static void detectLeakingThreads() {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Set<String> leakedThreads = new HashSet<String>();
Map<Integer, Exception> createdThreads = ThreadFactoryUtil.getCreatedThreads();
for(Thread thread : threadSet) {
if(thread.isAlive() && thread.getName().startsWith("Ehcache [")) {
int hash = System.identityHashCode(thread);
String stackTrace = null;
if(createdThreads != null) {
Exception exception = createdThreads.get(hash);
StringWriter errors = new StringWriter();
exception.printStackTrace(new PrintWriter(errors));
stackTrace = errors.toString();
}
leakedThreads.add(thread + "(" + hash + ")" + stackTrace);
}
}
assertThat(leakedThreads).isEmpty();
}
}