fr.xebia.springframework.concurrent.ThreadPoolExecutorFactory.java Source code

Java tutorial

Introduction

Here is the source code for fr.xebia.springframework.concurrent.ThreadPoolExecutorFactory.java

Source

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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 fr.xebia.springframework.concurrent;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.core.style.ToStringCreator;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.naming.SelfNaming;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * 
 * @author <a href="mailto:cyrille@cyrilleleclerc.com">Cyrille Le Clerc</a>
 */
public class ThreadPoolExecutorFactory extends AbstractFactoryBean<ThreadPoolExecutor>
        implements FactoryBean<ThreadPoolExecutor>, BeanNameAware {

    private static class CountingRejectedExecutionHandler implements RejectedExecutionHandler {

        private final AtomicInteger rejectedExecutionCount = new AtomicInteger();

        private final RejectedExecutionHandler rejectedExecutionHandler;

        public CountingRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
            super();
            this.rejectedExecutionHandler = rejectedExecutionHandler;
        }

        public int getRejectedExecutionCount() {
            return rejectedExecutionCount.get();
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            rejectedExecutionCount.incrementAndGet();
            rejectedExecutionHandler.rejectedExecution(r, executor);
        }

        @Override
        public String toString() {
            return new ToStringCreator(this).append("rejectedExecutionCount", this.rejectedExecutionCount)
                    .append("rejectedExecutionHandler", this.rejectedExecutionHandler).toString();
        }

    }

    @ManagedResource
    public static class SpringJmxEnabledThreadPoolExecutor extends ThreadPoolExecutor implements SelfNaming {

        private ObjectName objectName;

        public SpringJmxEnabledThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                RejectedExecutionHandler rejectedExecutionHandler, ObjectName objectName) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,
                    new CountingRejectedExecutionHandler(rejectedExecutionHandler));
            this.objectName = objectName;
        }

        @Override
        @ManagedAttribute(description = "Returns the approximate number of threads that are actively executing tasks")
        public int getActiveCount() {
            return super.getActiveCount();
        }

        @ManagedAttribute(description = "Returns the approximate total number of tasks that have completed execution.")
        @Override
        public long getCompletedTaskCount() {
            return super.getCompletedTaskCount();
        }

        @ManagedAttribute(description = "Returns the core number of threads")
        @Override
        public int getCorePoolSize() {
            return super.getCorePoolSize();
        }

        @ManagedAttribute(description = "Returns the largest number of threads that have ever simultaneously been in the pool.")
        @Override
        public int getLargestPoolSize() {
            return super.getLargestPoolSize();
        }

        @ManagedAttribute(description = "Returns the maximum allowed number of threads")
        @Override
        public int getMaximumPoolSize() {
            return super.getMaximumPoolSize();
        }

        public ObjectName getObjectName() throws MalformedObjectNameException {
            return objectName;
        }

        @ManagedAttribute(description = "Returns the number of additional elements that this queue can "
                + "ideally (in the absence of memory or resource constraints) accept without  "
                + "blocking, or Integer.MAX_VALUE if there is no intrinsic limit.")
        public int getQueueRemainingCapacity() {
            return getQueue().remainingCapacity();
        }

        @ManagedAttribute(description = "Returns the number of tasks that has ever been rejected")
        public int getRejectedExecutionCount() {
            return ((CountingRejectedExecutionHandler) getRejectedExecutionHandler()).getRejectedExecutionCount();
        }

        @Override
        @ManagedAttribute(description = "Returns the approximate total number of tasks that have ever been scheduled for execution "
                + "(does not include the rejected tasks)")
        public long getTaskCount() {
            return super.getTaskCount();
        }

        @ManagedAttribute(description = "Sets the core number of threads. "
                + "If the new value is smaller than the current value, excess existing threads will be terminated when they next "
                + "become idle. If larger, new threads will, if needed, be started to execute any queued tasks.")
        @Override
        public void setCorePoolSize(int corePoolSize) {
            super.setCorePoolSize(corePoolSize);
        }

        @ManagedAttribute(description = "Sets the maximum allowed number of threads. "
                + "If the new value is smaller than the current value, excess existing threads will be "
                + "terminated when they next become idle.")
        @Override
        public void setMaximumPoolSize(int maximumPoolSize) {
            super.setMaximumPoolSize(maximumPoolSize);
        }

        @Override
        public String toString() {
            return new ToStringCreator(this).append("objectName", this.objectName)
                    .append("corePoolSize", this.getCorePoolSize())
                    .append("maximumPoolSize", this.getMaximumPoolSize())
                    .append("keepAliveTimeInMillis", this.getKeepAliveTime(TimeUnit.MILLISECONDS))
                    .append("queue", this.getQueue().getClass())
                    .append("rejectedExecutionHandler", this.getRejectedExecutionHandler()).toString();
        }
    }

    private String beanName;

    private int corePoolSize = 1;

    private long keepAliveTimeInSeconds;

    private int maximumPoolSize = Integer.MAX_VALUE;

    private int queueCapacity = Integer.MAX_VALUE;

    private Class<? extends RejectedExecutionHandler> rejectedExecutionHandlerClass = AbortPolicy.class;

    @Override
    protected ThreadPoolExecutor createInstance() throws Exception {
        Assert.isTrue(this.corePoolSize >= 0, "corePoolSize must be greater than or equal to zero");
        Assert.isTrue(this.maximumPoolSize > 0, "maximumPoolSize must be greater than zero");
        Assert.isTrue(this.maximumPoolSize >= this.corePoolSize,
                "maximumPoolSize must be greater than or equal to corePoolSize");
        Assert.isTrue(this.queueCapacity >= 0, "queueCapacity must be greater than or equal to zero");

        CustomizableThreadFactory threadFactory = new CustomizableThreadFactory(this.beanName + "-");
        threadFactory.setDaemon(true);

        BlockingQueue<Runnable> blockingQueue;
        if (queueCapacity == 0) {
            blockingQueue = new SynchronousQueue<Runnable>();
        } else {
            blockingQueue = new LinkedBlockingQueue<Runnable>(queueCapacity);
        }
        ThreadPoolExecutor instance = new SpringJmxEnabledThreadPoolExecutor(corePoolSize, //
                maximumPoolSize, //
                keepAliveTimeInSeconds, //
                TimeUnit.SECONDS, //
                blockingQueue, //
                threadFactory, //
                rejectedExecutionHandlerClass.newInstance(), //
                new ObjectName("java.util.concurrent:type=ThreadPoolExecutor,name=" + beanName));

        return instance;
    }

    @Override
    protected void destroyInstance(ThreadPoolExecutor instance) throws Exception {
        instance.shutdown();
    }

    @Override
    public Class<?> getObjectType() {
        return SpringJmxEnabledThreadPoolExecutor.class;
    }

    public void setBeanName(String name) {
        this.beanName = name;
    }

    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public void setKeepAliveTimeInSeconds(long keepAliveTimeInSeconds) {
        this.keepAliveTimeInSeconds = keepAliveTimeInSeconds;
    }

    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    /**
     * @deprecated Use {@link #setCorePoolSize(int)} and
     *             {@link #setMaximumPoolSize(int)} or
     *             {@link #setPoolSize(String)}.
     */
    @Deprecated
    public void setNbThreads(int nbThreads) {
        this.corePoolSize = nbThreads;
        this.maximumPoolSize = nbThreads;
    }

    public void setPoolSize(String poolSize) {
        if (!StringUtils.hasText(poolSize)) {
            return;
        }

        switch (StringUtils.countOccurrencesOf(poolSize, "-")) {
        case 0:
            this.corePoolSize = Integer.parseInt(poolSize);
            this.maximumPoolSize = this.corePoolSize;
            break;
        case 1:
            String[] splittedPoolSize = StringUtils.split(poolSize, "-");
            this.corePoolSize = Integer.parseInt(splittedPoolSize[0]);
            this.maximumPoolSize = Integer.parseInt(splittedPoolSize[1]);
            break;
        default:
            throw new BeanCreationException(this.beanName,
                    "Invalid pool-size value [" + poolSize + "]: only single maximum integer "
                            + "(e.g. \"5\") and minimum-maximum range (e.g. \"3-5\") are supported.");
        }
    }

    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

    public void setRejectedExecutionHandlerClass(
            Class<? extends RejectedExecutionHandler> rejectedExecutionHandlerClass) {
        this.rejectedExecutionHandlerClass = rejectedExecutionHandlerClass;
    }
}