package io.cattle.platform.spring.resource;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.util.type.Named;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import com.netflix.config.DynamicBooleanProperty;
import com.netflix.config.DynamicIntProperty;
import fr.xebia.springframework.concurrent.ThreadPoolExecutorFactory.SpringJmxEnabledThreadPoolExecutor;
public class SpringConfigurableExecutorService extends SpringJmxEnabledThreadPoolExecutor implements Named {
String name;
public SpringConfigurableExecutorService(String name, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler,
ObjectName objectName) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedExecutionHandler, objectName);
this.name = name;
}
public static SpringConfigurableExecutorService byName(String name) throws MalformedObjectNameException {
return byName(name, new ThreadPoolExecutor.AbortPolicy());
}
public static SpringConfigurableExecutorService byName(String name, RejectedExecutionHandler rejectedExecutionHandler) throws MalformedObjectNameException {
final DynamicIntProperty corePoolSize = ArchaiusUtil.getInt("pool." + name.toLowerCase() + ".core.size");
final DynamicIntProperty maxPoolSize = ArchaiusUtil.getInt("pool." + name.toLowerCase() + ".max.size");
final DynamicIntProperty keepAliveTime = ArchaiusUtil.getInt("pool." + name.toLowerCase() + ".keep.alive");
DynamicIntProperty queueSize = ArchaiusUtil.getInt("pool." + name.toLowerCase() + ".queue.size");
DynamicBooleanProperty priorityQueue = ArchaiusUtil.getBoolean("pool." + name.toLowerCase() + ".priority.queue");
BlockingQueue<Runnable> workQueue = new SynchronousQueue<>();
int size = queueSize.get();
if (size > 0) {
if (priorityQueue.get()) {
workQueue = new BoundedQueue<>(size);
} else {
workQueue = new LinkedBlockingQueue<>(size);
}
} else if (size < 0) {
workQueue = new LinkedBlockingQueue<>();
}
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory(name + "-");
threadFactory.setDaemon(true);
final SpringConfigurableExecutorService es =
new SpringConfigurableExecutorService(name, corePoolSize.get(), maxPoolSize.get(),
keepAliveTime.get(), TimeUnit.SECONDS, workQueue, threadFactory, rejectedExecutionHandler,
new ObjectName("java.util.concurrent:type=ThreadPoolExecutor,name=" + name));
corePoolSize.addCallback(new Runnable() {
@Override
public void run() {
es.setCorePoolSize(corePoolSize.get());
}
});
maxPoolSize.addCallback(new Runnable() {
@Override
public void run() {
es.setMaximumPoolSize(maxPoolSize.get());
}
});
keepAliveTime.addCallback(new Runnable() {
@Override
public void run() {
es.setKeepAliveTime(keepAliveTime.get(), TimeUnit.SECONDS);
}
});
return es;
}
public static class BoundedQueue<E> extends PriorityBlockingQueue<E> {
private static final long serialVersionUID = 8385439019119907779L;
int capacity;
public BoundedQueue(int capacity) {
super();
this.capacity = capacity;
}
@Override
public boolean offer(E e) {
// Fuzzy, but good enough
if (size() > capacity) {
return false;
}
return super.offer(e);
}
@Override
public int remainingCapacity() {
return capacity - size();
}
}
@Override
public String getName() {
return name;
}
}