StandardThreadExecutor.java
/*
* 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.catalina.core;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.catalina.Executor;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.ResizableExecutor;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
public class StandardThreadExecutor extends LifecycleMBeanBase implements Executor, ResizableExecutor {
protected static final StringManager sm = StringManager.getManager(StandardThreadExecutor.class);
// ---------------------------------------------- Properties
/**
* Default thread priority
*/
protected int threadPriority = Thread.NORM_PRIORITY;
/**
* Run threads in daemon or non-daemon state
*/
protected boolean daemon = true;
/**
* Default name prefix for the thread name
*/
protected String namePrefix = "tomcat-exec-";
/**
* max number of threads
*/
protected int maxThreads = 200;
/**
* min number of threads
*/
protected int minSpareThreads = 25;
/**
* idle time in milliseconds
*/
protected int maxIdleTime = 60000;
/**
* The executor we use for this component
*/
protected ThreadPoolExecutor executor = null;
/**
* the name of this thread pool
*/
protected String name;
/**
* The maximum number of elements that can queue up before we reject them
*/
protected int maxQueueSize = Integer.MAX_VALUE;
/**
* After a context is stopped, threads in the pool are renewed. To avoid renewing all threads at the same time, this
* delay is observed between 2 threads being renewed.
*/
protected long threadRenewalDelay = org.apache.tomcat.util.threads.Constants.DEFAULT_THREAD_RENEWAL_DELAY;
private TaskQueue taskqueue = null;
// ---------------------------------------------- Constructors
public StandardThreadExecutor() {
// empty constructor for the digester
}
// ---------------------------------------------- Public Methods
/**
* Start the component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error that prevents this component from being
* used
*/
@Override
protected void startInternal() throws LifecycleException {
taskqueue = new TaskQueue(maxQueueSize);
TaskThreadFactory tf = new TaskThreadFactory(namePrefix, daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,
taskqueue, tf);
executor.setThreadRenewalDelay(threadRenewalDelay);
taskqueue.setParent(executor);
setState(LifecycleState.STARTING);
}
/**
* Stop the component and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error that needs to be reported
*/
@Override
protected void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
if (executor != null) {
executor.shutdownNow();
}
executor = null;
taskqueue = null;
}
@Override
public void execute(Runnable command) {
if (executor != null) {
// Note any RejectedExecutionException due to the use of TaskQueue
// will be handled by the o.a.t.u.threads.ThreadPoolExecutor
executor.execute(command);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
public void contextStopping() {
if (executor != null) {
executor.contextStopping();
}
}
public int getThreadPriority() {
return threadPriority;
}
public boolean isDaemon() {
return daemon;
}
public String getNamePrefix() {
return namePrefix;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
@Override
public int getMaxThreads() {
return maxThreads;
}
public int getMinSpareThreads() {
return minSpareThreads;
}
@Override
public String getName() {
return name;
}
public void setThreadPriority(int threadPriority) {
this.threadPriority = threadPriority;
}
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
public void setNamePrefix(String namePrefix) {
this.namePrefix = namePrefix;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
if (executor != null) {
executor.setKeepAliveTime(maxIdleTime, TimeUnit.MILLISECONDS);
}
}
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
if (executor != null) {
executor.setMaximumPoolSize(maxThreads);
}
}
public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
if (executor != null) {
executor.setCorePoolSize(minSpareThreads);
}
}
public void setName(String name) {
this.name = name;
}
public void setMaxQueueSize(int size) {
this.maxQueueSize = size;
}
public int getMaxQueueSize() {
return maxQueueSize;
}
public long getThreadRenewalDelay() {
return threadRenewalDelay;
}
public void setThreadRenewalDelay(long threadRenewalDelay) {
this.threadRenewalDelay = threadRenewalDelay;
if (executor != null) {
executor.setThreadRenewalDelay(threadRenewalDelay);
}
}
// Statistics from the thread pool
@Override
public int getActiveCount() {
return (executor != null) ? executor.getActiveCount() : 0;
}
public long getCompletedTaskCount() {
return (executor != null) ? executor.getCompletedTaskCount() : 0;
}
public int getCorePoolSize() {
return (executor != null) ? executor.getCorePoolSize() : 0;
}
public int getLargestPoolSize() {
return (executor != null) ? executor.getLargestPoolSize() : 0;
}
@Override
public int getPoolSize() {
return (executor != null) ? executor.getPoolSize() : 0;
}
public int getQueueSize() {
return (executor != null) ? executor.getQueue().size() : -1;
}
@Override
public boolean resizePool(int corePoolSize, int maximumPoolSize) {
if (executor == null) {
return false;
}
executor.setCorePoolSize(corePoolSize);
executor.setMaximumPoolSize(maximumPoolSize);
return true;
}
@Override
public boolean resizeQueue(int capacity) {
return false;
}
@Override
protected String getDomainInternal() {
// No way to navigate to Engine. Needs to have domain set.
return null;
}
@Override
protected String getObjectNameKeyProperties() {
return "type=Executor,name=" + getName();
}
@Override
public void shutdown() {
// Controlled by Lifecycle instead
}
@Override
public List<Runnable> shutdownNow() {
// Controlled by Lifecycle instead
return Collections.emptyList();
}
@Override
public boolean isShutdown() {
if (executor != null) {
return executor.isShutdown();
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public boolean isTerminated() {
if (executor != null) {
return executor.isTerminated();
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public <T> Future<T> submit(Callable<T> task) {
if (executor != null) {
return executor.submit(task);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
if (executor != null) {
return executor.submit(task, result);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public Future<?> submit(Runnable task) {
if (executor != null) {
return executor.submit(task);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
if (executor != null) {
return executor.invokeAll(tasks);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
if (executor != null) {
return executor.invokeAll(tasks, timeout, unit);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
if (executor != null) {
return executor.invokeAny(tasks);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (executor != null) {
return executor.invokeAny(tasks, timeout, unit);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
}
}