/*
* Copyright 2013 Rackspace
*
* 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 com.rackspacecloud.blueflood.concurrent;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.rackspacecloud.blueflood.utils.TimeValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolBuilder {
private static final Logger log = LoggerFactory.getLogger(ThreadPoolBuilder.class);
/** Used to ensure that the thread pools have unique name. */
private static final ConcurrentHashMap<String, AtomicInteger> nameMap = new ConcurrentHashMap<String, AtomicInteger>();
private static final String DEFAULT_NAME = "Threadpool";
private int corePoolSize = 10;
private int maxPoolSize = 10;
private int queueSize = 0;
private TimeValue keepAliveTime = new TimeValue(30, TimeUnit.SECONDS);
private RejectedExecutionHandler rejectedHandler = new ThreadPoolExecutor.CallerRunsPolicy();
private Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
log.error(e.getMessage(), e);
}
};
private String threadNameFormat = null;
private String poolName = null;
public ThreadPoolBuilder() {
withName(DEFAULT_NAME);
}
public ThreadPoolBuilder withCorePoolSize(int size) {
this.corePoolSize = size;
return this;
}
public ThreadPoolBuilder withMaxPoolSize(int size) {
this.maxPoolSize = size;
return this;
}
public ThreadPoolBuilder withSynchronousQueue() {
this.queueSize = 0;
return this;
}
public ThreadPoolBuilder withUnboundedQueue() {
this.queueSize = -1;
return this;
}
public ThreadPoolBuilder withBoundedQueue(int size) {
this.queueSize = size;
return this;
}
public ThreadPoolBuilder withKeepAliveTime(TimeValue time) {
this.keepAliveTime = time;
return this;
}
public ThreadPoolBuilder withRejectedHandler(RejectedExecutionHandler rejectedHandler) {
this.rejectedHandler = rejectedHandler;
return this;
}
/**
* Set the threadpool name. Used to generate metric names and thread names.
*/
public ThreadPoolBuilder withName(String name) {
// ensure we've got a spot to put the thread id.
if (!name.contains("%d")) {
name = name + "-%d";
}
nameMap.putIfAbsent(name, new AtomicInteger(0));
int id = nameMap.get(name).incrementAndGet();
this.poolName = String.format(name, id);
if (id > 1) {
this.threadNameFormat = name.replace("%d", id + "-%d");
} else {
this.threadNameFormat = name;
}
return this;
}
public ThreadPoolBuilder withExceptionHandler(Thread.UncaughtExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
return this;
}
public ThreadPoolExecutor build() {
BlockingQueue<Runnable> workQueue;
switch (this.queueSize) {
case 0: workQueue = new SynchronousQueue<Runnable>();
break;
case -1: workQueue = new LinkedBlockingQueue<Runnable>();
break;
default: workQueue = new ArrayBlockingQueue<Runnable>(queueSize);
break;
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize,
keepAliveTime.getValue(), keepAliveTime.getUnit(),
workQueue,
new ThreadFactoryBuilder().setNameFormat(threadNameFormat).setPriority(Thread.NORM_PRIORITY).setUncaughtExceptionHandler(exceptionHandler).build(),
rejectedHandler);
InstrumentedThreadPoolExecutor.instrument(executor, poolName);
return executor;
}
}